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: diff --git a/src/d3d11/d3d11_context.cpp b/src/d3d11/d3d11_context.cpp index 9e922901..62bb9844 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"); @@ -1237,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(); } } @@ -2290,8 +2298,11 @@ namespace dxvk { const D3D11_RECT* pRects) { m_state.rs.numScissors = NumRects; - for (uint32_t i = 0; i < NumRects; i++) - m_state.rs.scissors.at(i) = pRects[i]; + for (uint32_t i = 0; i < NumRects; i++) { + if (pRects[i].bottom >= pRects[i].top + && pRects[i].right >= pRects[i].left) + m_state.rs.scissors.at(i) = pRects[i]; + } if (m_state.rs.state != nullptr) { D3D11_RASTERIZER_DESC rsDesc; @@ -2554,17 +2565,22 @@ namespace dxvk { } for (uint32_t i = 0; i < m_state.rs.numViewports; i++) { - // TODO D3D11 docs aren't clear about what should happen - // when there are undefined scissor rects for a viewport. - // Figure out what it does on Windows. if (enableScissorTest && (i < m_state.rs.numScissors)) { - const D3D11_RECT& sr = m_state.rs.scissors.at(i); + D3D11_RECT sr = m_state.rs.scissors.at(i); - scissors.at(i) = VkRect2D { - VkOffset2D { sr.left, sr.top }, - VkExtent2D { - static_cast(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 }, 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 { diff --git a/src/d3d11/d3d11_device.cpp b/src/d3d11/d3d11_device.cpp index be397366..4ad4c8c7 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) { @@ -413,9 +413,6 @@ namespace dxvk { viewInfo.numLevels = desc.Texture2D.MipLevels; viewInfo.minLayer = 0; viewInfo.numLayers = 1; - - if (m_dxbcOptions.test(DxbcOption::ForceTex2DArray)) - viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY; break; case D3D11_SRV_DIMENSION_TEXTURE2DARRAY: @@ -432,9 +429,6 @@ namespace dxvk { viewInfo.numLevels = 1; viewInfo.minLayer = 0; viewInfo.numLayers = 1; - - if (m_dxbcOptions.test(DxbcOption::ForceTex2DArray)) - viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY; break; case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY: @@ -624,9 +618,6 @@ namespace dxvk { viewInfo.numLevels = 1; viewInfo.minLayer = 0; viewInfo.numLayers = 1; - - if (m_dxbcOptions.test(DxbcOption::ForceTex2DArray)) - viewInfo.type = VK_IMAGE_VIEW_TYPE_2D_ARRAY; break; case D3D11_UAV_DIMENSION_TEXTURE2DARRAY: @@ -1124,7 +1115,10 @@ namespace dxvk { ID3D11GeometryShader** ppGeometryShader) { InitReturnPtr(ppGeometryShader); Logger::err("D3D11Device::CreateGeometryShaderWithStreamOutput: Not implemented"); - return E_NOTIMPL; + + // Returning S_OK instead of an error fixes some issues + // with Overwatch until this is properly implemented + return m_d3d11Options.test(D3D11Option::FakeStreamOutSupport) ? S_OK : E_NOTIMPL; } @@ -1813,6 +1807,8 @@ namespace dxvk { enabled.shaderFloat64 = supported.shaderFloat64; enabled.shaderInt64 = supported.shaderInt64; enabled.tessellationShader = VK_TRUE; + // TODO enable unconditionally once RADV gains support + enabled.shaderStorageImageMultisample = supported.shaderStorageImageMultisample; enabled.shaderStorageImageReadWithoutFormat = supported.shaderStorageImageReadWithoutFormat; enabled.shaderStorageImageWriteWithoutFormat = VK_TRUE; } @@ -1849,8 +1845,10 @@ namespace dxvk { void D3D11Device::InitBuffer( D3D11Buffer* pBuffer, const D3D11_SUBRESOURCE_DATA* pInitialData) { - const DxvkBufferSlice bufferSlice - = pBuffer->GetBufferSlice(); + const DxvkBufferSlice bufferSlice = pBuffer->GetBufferSlice(); + + D3D11_BUFFER_DESC desc; + pBuffer->GetDesc(&desc); if (pInitialData != nullptr && pInitialData->pSysMem != nullptr) { LockResourceInitContext(); @@ -1861,14 +1859,25 @@ namespace dxvk { bufferSlice.length(), pInitialData->pSysMem); + UnlockResourceInitContext(1); + } else if (desc.Usage == D3D11_USAGE_DEFAULT) { + LockResourceInitContext(); + + m_resourceInitContext->clearBuffer( + bufferSlice.buffer(), + bufferSlice.offset(), + bufferSlice.length(), + 0u); + UnlockResourceInitContext(1); } } void D3D11Device::InitTexture( - const Rc& 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( 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: 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; 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/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; }; /** diff --git a/src/dxbc/dxbc_compiler.cpp b/src/dxbc/dxbc_compiler.cpp index 57bd3d2a..abeb1658 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(); } @@ -277,6 +284,9 @@ namespace dxvk { case DxbcOpcode::DclThreadGroup: return this->emitDclThreadGroup(ins); + case DxbcOpcode::DclGsInstanceCount: + return this->emitDclGsInstanceCount(ins); + default: Logger::warn( str::format("DxbcCompiler: Unhandled opcode: ", @@ -456,7 +466,6 @@ namespace dxvk { } break; case DxbcOperandType::InputCoverageMask: { - m_module.enableCapability(spv::CapabilitySampleRateShading); m_ps.builtinSampleMaskIn = emitNewBuiltinVariable({ { DxbcScalarType::Uint32, 1, 1 }, spv::StorageClassInput }, @@ -465,7 +474,6 @@ namespace dxvk { } break; case DxbcOperandType::OutputCoverageMask: { - m_module.enableCapability(spv::CapabilitySampleRateShading); m_ps.builtinSampleMaskOut = emitNewBuiltinVariable({ { DxbcScalarType::Uint32, 1, 1 }, spv::StorageClassOutput }, @@ -547,6 +555,14 @@ namespace dxvk { // output arrays, so there's nothing left to do. } break; + case DxbcOperandType::InputGsInstanceId: { + m_gs.builtinInvocationId = emitNewBuiltinVariable({ + { DxbcScalarType::Uint32, 1, 0 }, + spv::StorageClassInput }, + spv::BuiltInInvocationId, + "vInstanceID"); + } break; + default: Logger::err(str::format( "DxbcCompiler: Unsupported operand type declaration: ", @@ -819,7 +835,6 @@ namespace dxvk { case DxbcResourceDim::Buffer: m_module.enableCapability(spv::CapabilityImageBuffer); break; case DxbcResourceDim::Texture1D: m_module.enableCapability(spv::CapabilityImage1D); break; case DxbcResourceDim::Texture1DArr: m_module.enableCapability(spv::CapabilityImage1D); break; - case DxbcResourceDim::TextureCube: m_module.enableCapability(spv::CapabilityImageCubeArray); break; case DxbcResourceDim::TextureCubeArr: m_module.enableCapability(spv::CapabilityImageCubeArray); break; case DxbcResourceDim::Texture2DMs: m_module.enableCapability(spv::CapabilityImageMSArray); break; case DxbcResourceDim::Texture2DMsArr: m_module.enableCapability(spv::CapabilityImageMSArray); break; @@ -955,7 +970,7 @@ namespace dxvk { const DxbcScalarType sampledType = DxbcScalarType::Uint32; const uint32_t sampledTypeId = getScalarTypeId(sampledType); - const DxbcImageInfo typeInfo = { spv::DimBuffer, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_MAX_ENUM }; + const DxbcImageInfo typeInfo = { spv::DimBuffer, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_MAX_ENUM }; // Declare the resource type const uint32_t resTypeId = m_module.defImageType(sampledTypeId, @@ -1221,6 +1236,13 @@ namespace dxvk { } + void DxbcCompiler::emitDclGsInstanceCount(const DxbcShaderInstruction& ins) { + // dcl_gs_instance_count has one operand: + // (imm0) Number of geometry shader invocations + m_module.setInvocations(m_entryPointId, ins.imm[0].u32); + } + + uint32_t DxbcCompiler::emitDclUavCounter(uint32_t regId) { // Declare a structure type which holds the UAV counter if (m_uavCtrStructType == 0) { @@ -1981,9 +2003,27 @@ namespace dxvk { // (dst0) Register that receives the result // (dst1) Destination u# or g# register // (srcX) As above + const DxbcBufferInfo bufferInfo = getBufferInfo(ins.dst[ins.dstCount - 1]); + const bool isImm = ins.dstCount == 2; const bool isUav = ins.dst[ins.dstCount - 1].type == DxbcOperandType::UnorderedAccessView; + // Perform atomic operations on UAVs only if the UAV + // is bound and if there is nothing else stopping us. + DxbcConditional cond; + + if (isUav) { + uint32_t writeTest = emitUavWriteTest(bufferInfo); + + cond.labelIf = m_module.allocateId(); + cond.labelEnd = m_module.allocateId(); + + m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone); + m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd); + + m_module.opLabel(cond.labelIf); + } + // Retrieve destination pointer for the atomic operation> const DxbcRegisterPointer pointer = emitGetAtomicPointer( ins.dst[ins.dstCount - 1], ins.src[0]); @@ -2104,6 +2144,12 @@ namespace dxvk { // register if this is an imm_atomic_* opcode. if (isImm) emitRegisterStore(ins.dst[0], value); + + // End conditional block + if (isUav) { + m_module.opBranch(cond.labelEnd); + m_module.opLabel (cond.labelEnd); + } } @@ -2111,12 +2157,25 @@ namespace dxvk { // imm_atomic_alloc and imm_atomic_consume have the following operands: // (dst0) The register that will hold the old counter value // (dst1) The UAV whose counter is going to be modified - // TODO check if the corresponding UAV is bound + const DxbcBufferInfo bufferInfo = getBufferInfo(ins.dst[1]); + const uint32_t registerId = ins.dst[1].idx[0].offset; if (m_uavs.at(registerId).ctrId == 0) m_uavs.at(registerId).ctrId = emitDclUavCounter(registerId); + // Only perform the operation if the UAV is bound + uint32_t writeTest = emitUavWriteTest(bufferInfo); + + DxbcConditional cond; + cond.labelIf = m_module.allocateId(); + cond.labelEnd = m_module.allocateId(); + + m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone); + m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd); + + m_module.opLabel(cond.labelIf); + // Get a pointer to the atomic counter in question DxbcRegisterInfo ptrType; ptrType.type.ctype = DxbcScalarType::Uint32; @@ -2165,6 +2224,10 @@ namespace dxvk { // Store the result emitRegisterStore(ins.dst[0], value); + + // End conditional block + m_module.opBranch(cond.labelEnd); + m_module.opLabel (cond.labelEnd); } @@ -3232,23 +3295,37 @@ namespace dxvk { // (dst0) The destination UAV // (src0) The texture or buffer coordinates // (src1) The value to store - const uint32_t registerId = ins.dst[0].idx[0].offset; - const DxbcUav uavInfo = m_uavs.at(registerId); + const DxbcBufferInfo uavInfo = getBufferInfo(ins.dst[0]); + + // Execute write op only if the UAV is bound + uint32_t writeTest = emitUavWriteTest(uavInfo); + + DxbcConditional cond; + cond.labelIf = m_module.allocateId(); + cond.labelEnd = m_module.allocateId(); + + m_module.opSelectionMerge (cond.labelEnd, spv::SelectionControlMaskNone); + m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd); + + m_module.opLabel(cond.labelIf); // Load texture coordinates - DxbcRegisterValue texCoord = emitLoadTexCoord( - ins.src[0], uavInfo.imageInfo); + DxbcRegisterValue texCoord = emitLoadTexCoord(ins.src[0], uavInfo.image); // Load the value that will be written to the image. We'll // have to cast it to the component type of the image. const DxbcRegisterValue texValue = emitRegisterBitcast( emitRegisterLoad(ins.src[1], DxbcRegMask(true, true, true, true)), - uavInfo.sampledType); + uavInfo.stype); // Write the given value to the image m_module.opImageWrite( - m_module.opLoad(uavInfo.imageTypeId, uavInfo.varId), + m_module.opLoad(uavInfo.typeId, uavInfo.varId), texCoord.id, texValue.id, SpirvImageOperands()); + + // End conditional block + m_module.opBranch(cond.labelEnd); + m_module.opLabel (cond.labelEnd); } @@ -3258,29 +3335,21 @@ namespace dxvk { const DxbcRegisterValue condition = emitRegisterLoad( ins.src[0], DxbcRegMask(true, false, false, false)); - const DxbcRegisterValue zeroTest = emitRegisterZeroTest( - condition, ins.controls.zeroTest()); - // Declare the 'if' block. We do not know if there // will be an 'else' block or not, so we'll assume // that there is one and leave it empty otherwise. DxbcCfgBlock block; block.type = DxbcCfgBlockType::If; + block.b_if.ztestId = emitRegisterZeroTest(condition, ins.controls.zeroTest()).id; block.b_if.labelIf = m_module.allocateId(); - block.b_if.labelElse = m_module.allocateId(); + block.b_if.labelElse = 0; block.b_if.labelEnd = m_module.allocateId(); - block.b_if.hadElse = false; + block.b_if.headerPtr = m_module.getInsertionPtr(); m_controlFlowBlocks.push_back(block); - m_module.opSelectionMerge( - block.b_if.labelEnd, - spv::SelectionControlMaskNone); - - m_module.opBranchConditional( - zeroTest.id, - block.b_if.labelIf, - block.b_if.labelElse); - + // We'll insert the branch instruction when closing + // the block, since we don't know whether or not an + // else block is needed right now. m_module.opLabel(block.b_if.labelIf); } @@ -3288,13 +3357,13 @@ namespace dxvk { void DxbcCompiler::emitControlFlowElse(const DxbcShaderInstruction& ins) { if (m_controlFlowBlocks.size() == 0 || m_controlFlowBlocks.back().type != DxbcCfgBlockType::If - || m_controlFlowBlocks.back().b_if.hadElse) + || m_controlFlowBlocks.back().b_if.labelElse != 0) throw DxvkError("DxbcCompiler: 'Else' without 'If' found"); // Set the 'Else' flag so that we do // not insert a dummy block on 'EndIf' DxbcCfgBlock& block = m_controlFlowBlocks.back(); - block.b_if.hadElse = true; + block.b_if.labelElse = m_module.allocateId(); // Close the 'If' block by branching to // the merge block we declared earlier @@ -3309,21 +3378,28 @@ namespace dxvk { throw DxvkError("DxbcCompiler: 'EndIf' without 'If' found"); // Remove the block from the stack, it's closed - const DxbcCfgBlock block = m_controlFlowBlocks.back(); + DxbcCfgBlock block = m_controlFlowBlocks.back(); m_controlFlowBlocks.pop_back(); + // Write out the 'if' header + m_module.beginInsertion(block.b_if.headerPtr); + + m_module.opSelectionMerge( + block.b_if.labelEnd, + spv::SelectionControlMaskNone); + + m_module.opBranchConditional( + block.b_if.ztestId, + block.b_if.labelIf, + block.b_if.labelElse != 0 + ? block.b_if.labelElse + : block.b_if.labelEnd); + + m_module.endInsertion(); + // End the active 'if' or 'else' block m_module.opBranch(block.b_if.labelEnd); - - // If there was no 'else' block in this construct, we still - // have to declare it because we used it as a branch target. - if (!block.b_if.hadElse) { - m_module.opLabel (block.b_if.labelElse); - m_module.opBranch(block.b_if.labelEnd); - } - - // Declare the merge block - m_module.opLabel(block.b_if.labelEnd); + m_module.opLabel (block.b_if.labelEnd); } @@ -3589,21 +3665,26 @@ namespace dxvk { const DxbcRegisterValue zeroTest = emitRegisterZeroTest( condition, ins.controls.zeroTest()); - // Insert a Pseudo-'If' block - const uint32_t discardBlock = m_module.allocateId(); - const uint32_t mergeBlock = m_module.allocateId(); - - m_module.opSelectionMerge(mergeBlock, - spv::SelectionControlMaskNone); - - m_module.opBranchConditional( - zeroTest.id, discardBlock, mergeBlock); - - // OpKill terminates the block - m_module.opLabel(discardBlock); - m_module.opKill(); - - m_module.opLabel(mergeBlock); + if (m_ps.killState == 0) { + DxbcConditional cond; + cond.labelIf = m_module.allocateId(); + cond.labelEnd = m_module.allocateId(); + + m_module.opSelectionMerge(cond.labelIf, spv::SelectionControlMaskNone); + m_module.opBranchConditional(zeroTest.id, cond.labelIf, cond.labelEnd); + + // OpKill terminates the block + m_module.opLabel(cond.labelIf); + m_module.opKill(); + + m_module.opLabel(cond.labelEnd); + } else { + uint32_t typeId = m_module.defBoolType(); + + uint32_t killState = m_module.opLoad (typeId, m_ps.killState); + killState = m_module.opLogicalOr(typeId, killState, zeroTest.id); + m_module.opStore(m_ps.killState, killState); + } } @@ -4285,6 +4366,11 @@ namespace dxvk { return DxbcRegisterPointer { { DxbcScalarType::Uint32, 1 }, getCurrentHsForkJoinPhase()->instanceIdPtr }; + + case DxbcOperandType::InputGsInstanceId: + return DxbcRegisterPointer { + { DxbcScalarType::Uint32, 1 }, + m_gs.builtinInvocationId }; default: throw DxvkError(str::format( @@ -4441,11 +4527,27 @@ namespace dxvk { // Cast source value to the expected data type value = emitRegisterBitcast(value, DxbcScalarType::Uint32); - // Shared memory is not accessed through a texel buffer view - const bool isTgsm = operand.type == DxbcOperandType::ThreadGroupSharedMemory; + // Thread Group Shared Memory is not accessed through a texel buffer view + const bool isUav = operand.type == DxbcOperandType::UnorderedAccessView; - const uint32_t bufferId = isTgsm - ? 0 : m_module.opLoad(bufferInfo.typeId, bufferInfo.varId); + // Perform UAV writes only if the UAV is bound and if there + // is nothing else preventing us from writing to it. + DxbcConditional cond; + + if (isUav) { + uint32_t writeTest = emitUavWriteTest(bufferInfo); + + cond.labelIf = m_module.allocateId(); + cond.labelEnd = m_module.allocateId(); + + m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone); + m_module.opBranchConditional(writeTest, cond.labelIf, cond.labelEnd); + + m_module.opLabel(cond.labelIf); + } + + // Perform the actual write operation + const uint32_t bufferId = isUav ? m_module.opLoad(bufferInfo.typeId, bufferInfo.varId) : 0; const uint32_t scalarTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 1 }); const uint32_t vectorTypeId = getVectorTypeId({ DxbcScalarType::Uint32, 4 }); @@ -4494,6 +4596,12 @@ namespace dxvk { srcComponentIndex += 1; } } + + // End conditional block + if (isUav) { + m_module.opBranch(cond.labelEnd); + m_module.opLabel (cond.labelEnd); + } } @@ -4538,15 +4646,21 @@ namespace dxvk { DxbcRegisterValue DxbcCompiler::emitQueryTextureSamples( const DxbcRegister& resource) { - const DxbcBufferInfo info = getBufferInfo(resource); - - DxbcRegisterValue result; - result.type.ctype = DxbcScalarType::Uint32; - result.type.ccount = 1; - result.id = m_module.opImageQuerySamples( - getVectorTypeId(result.type), - m_module.opLoad(info.typeId, info.varId)); - return result; + if (resource.type == DxbcOperandType::Rasterizer) { + // SPIR-V has no gl_NumSamples equivalent, so we have + // to work around it using a specialization constant + return getSpecConstant(DxvkSpecConstantId::RasterizerSampleCount); + } else { + DxbcBufferInfo info = getBufferInfo(resource); + + DxbcRegisterValue result; + result.type.ctype = DxbcScalarType::Uint32; + result.type.ccount = 1; + result.id = m_module.opImageQuerySamples( + getVectorTypeId(result.type), + m_module.opLoad(info.typeId, info.varId)); + return result; + } } @@ -4570,15 +4684,6 @@ namespace dxvk { m_module.opLoad(info.typeId, info.varId)); } - if (info.image.array && !info.image.layered) { - const uint32_t index = result.type.ccount - 1; - const uint32_t zero = m_module.constu32(0); - - result.id = m_module.opCompositeInsert( - getVectorTypeId(result.type), - zero, result.id, 1, &index); - } - return result; } @@ -4623,22 +4728,6 @@ namespace dxvk { coordVector, DxbcRegMask::firstN(dim)); } - if (imageInfo.array && !imageInfo.layered) { - const uint32_t index = dim - 1; - const uint32_t zero = [&] { - switch (coordVector.type.ctype) { - case DxbcScalarType::Sint32: return m_module.consti32(0); - case DxbcScalarType::Uint32: return m_module.constu32(0); - case DxbcScalarType::Float32: return m_module.constf32(0.0f); - default: throw DxvkError("Dxbc: Invalid tex coord type"); - } - }(); - - coordVector.id = m_module.opCompositeInsert( - getVectorTypeId(coordVector.type), - zero, coordVector.id, 1, &index); - } - return coordVector; } @@ -4784,6 +4873,43 @@ namespace dxvk { } + DxbcRegisterValue DxbcCompiler::getSpecConstant(DxvkSpecConstantId specId) { + const uint32_t specIdOffset = uint32_t(specId) - uint32_t(DxvkSpecConstantId::SpecConstantIdMin); + + // Look up spec constant in the array + DxbcRegisterValue value = m_specConstants.at(specIdOffset); + + if (value.id != 0) + return value; + + // Declare a new specialization constant if needed + DxbcSpecConstant info = getSpecConstantProperties(specId); + + value.type.ctype = info.ctype; + value.type.ccount = info.ccount; + value.id = m_module.specConst32( + getVectorTypeId(value.type), + info.value); + + m_module.decorateSpecId(value.id, uint32_t(specId)); + m_module.setDebugName(value.id, info.name); + + m_specConstants.at(specIdOffset) = value; + return value; + } + + + DxbcSpecConstant DxbcCompiler::getSpecConstantProperties(DxvkSpecConstantId specId) { + static const std::array s_specConstants = {{ + { DxbcScalarType::Uint32, 1, 1, "RasterizerSampleCount" }, + }}; + + return s_specConstants.at(uint32_t(specId) - uint32_t(DxvkSpecConstantId::SpecConstantIdMin)); + } + + void DxbcCompiler::emitInputSetup() { // Copy all defined v# registers into the input array const uint32_t vecTypeId = m_module.defVectorType(m_module.defFloatType(32), 4); @@ -5429,6 +5555,21 @@ namespace dxvk { } + uint32_t DxbcCompiler::emitUavWriteTest(const DxbcBufferInfo& uav) { + uint32_t typeId = m_module.defBoolType(); + uint32_t testId = uav.specId; + + if (m_ps.killState != 0) { + uint32_t killState = m_module.opLoad(typeId, m_ps.killState); + + testId = m_module.opLogicalAnd(typeId, testId, + m_module.opLogicalNot(typeId, killState)); + } + + return testId; + } + + void DxbcCompiler::emitInit() { // Set up common capabilities for all shaders m_module.enableCapability(spv::CapabilityShader); @@ -5633,6 +5774,16 @@ namespace dxvk { spv::BuiltInCullDistance, spv::StorageClassInput); + // We may have to defer kill operations to the end of + // the shader in order to keep derivatives correct. + if (m_analysis->usesKill && m_analysis->usesDerivatives && m_options.test(DxbcOption::DeferKill)) { + m_ps.killState = m_module.newVarInit( + m_module.defPointerType(m_module.defBoolType(), spv::StorageClassPrivate), + spv::StorageClassPrivate, m_module.constBool(false)); + + m_module.setDebugName(m_ps.killState, "ps_kill"); + } + // Main function of the pixel shader m_ps.functionId = m_module.allocateId(); m_module.setDebugName(m_ps.functionId, "ps_main"); @@ -5730,9 +5881,27 @@ namespace dxvk { this->emitInputSetup(); this->emitClipCullLoad(DxbcSystemValue::ClipDistance, m_clipDistances); this->emitClipCullLoad(DxbcSystemValue::CullDistance, m_cullDistances); + m_module.opFunctionCall( m_module.defVoidType(), m_ps.functionId, 0, nullptr); + + if (m_ps.killState != 0) { + DxbcConditional cond; + cond.labelIf = m_module.allocateId(); + cond.labelEnd = m_module.allocateId(); + + uint32_t killTest = m_module.opLoad(m_module.defBoolType(), m_ps.killState); + + m_module.opSelectionMerge(cond.labelEnd, spv::SelectionControlMaskNone); + m_module.opBranchConditional(killTest, cond.labelIf, cond.labelEnd); + + m_module.opLabel(cond.labelIf); + m_module.opKill(); + + m_module.opLabel(cond.labelEnd); + } + this->emitOutputSetup(); this->emitMainFunctionEnd(); } @@ -6256,25 +6425,20 @@ namespace dxvk { bool isUav) const { DxbcImageInfo typeInfo = [resourceType, isUav] () -> DxbcImageInfo { switch (resourceType) { - case DxbcResourceDim::Buffer: return { spv::DimBuffer, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_MAX_ENUM }; - case DxbcResourceDim::Texture1D: return { spv::Dim1D, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_1D }; - case DxbcResourceDim::Texture1DArr: return { spv::Dim1D, 1, 0, isUav ? 2u : 1u, 1u, VK_IMAGE_VIEW_TYPE_1D_ARRAY }; - case DxbcResourceDim::Texture2D: return { spv::Dim2D, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_2D }; - case DxbcResourceDim::Texture2DArr: return { spv::Dim2D, 1, 0, isUav ? 2u : 1u, 1u, VK_IMAGE_VIEW_TYPE_2D_ARRAY }; - case DxbcResourceDim::Texture2DMs: return { spv::Dim2D, 0, 1, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_2D }; - case DxbcResourceDim::Texture2DMsArr: return { spv::Dim2D, 1, 1, isUav ? 2u : 1u, 1u, VK_IMAGE_VIEW_TYPE_2D_ARRAY }; - case DxbcResourceDim::Texture3D: return { spv::Dim3D, 0, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_3D }; - case DxbcResourceDim::TextureCube: return { spv::DimCube, 1, 0, isUav ? 2u : 1u, 0u, VK_IMAGE_VIEW_TYPE_CUBE_ARRAY }; - case DxbcResourceDim::TextureCubeArr: return { spv::DimCube, 1, 0, isUav ? 2u : 1u, 1u, VK_IMAGE_VIEW_TYPE_CUBE_ARRAY }; + case DxbcResourceDim::Buffer: return { spv::DimBuffer, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_MAX_ENUM }; + case DxbcResourceDim::Texture1D: return { spv::Dim1D, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_1D }; + case DxbcResourceDim::Texture1DArr: return { spv::Dim1D, 1, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_1D_ARRAY }; + case DxbcResourceDim::Texture2D: return { spv::Dim2D, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D }; + case DxbcResourceDim::Texture2DArr: return { spv::Dim2D, 1, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D_ARRAY }; + case DxbcResourceDim::Texture2DMs: return { spv::Dim2D, 0, 1, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D }; + case DxbcResourceDim::Texture2DMsArr: return { spv::Dim2D, 1, 1, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_2D_ARRAY }; + case DxbcResourceDim::Texture3D: return { spv::Dim3D, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_3D }; + case DxbcResourceDim::TextureCube: return { spv::DimCube, 0, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_CUBE }; + case DxbcResourceDim::TextureCubeArr: return { spv::DimCube, 1, 0, isUav ? 2u : 1u, VK_IMAGE_VIEW_TYPE_CUBE_ARRAY }; default: throw DxvkError(str::format("DxbcCompiler: Unsupported resource type: ", resourceType)); } }(); - if (typeInfo.dim == spv::Dim2D && m_options.test(DxbcOption::ForceTex2DArray)) { - typeInfo.array = 1; - typeInfo.vtype = VK_IMAGE_VIEW_TYPE_2D_ARRAY; - } - return typeInfo; } diff --git a/src/dxbc/dxbc_compiler.h b/src/dxbc/dxbc_compiler.h index 9b7dd1f7..38115a08 100644 --- a/src/dxbc/dxbc_compiler.h +++ b/src/dxbc/dxbc_compiler.h @@ -96,6 +96,34 @@ namespace dxvk { }; + /** + * \brief Specialization constant properties + * + * Stores the name, data type and initial + * value of a specialization constant. + */ + struct DxbcSpecConstant { + DxbcScalarType ctype; + uint32_t ccount; + uint32_t value; + const char* name; + }; + + + /** + * \brief Helper struct for conditional execution + * + * Stores a set of labels required to implement either + * an if-then block or an if-then-else block. This is + * not used to implement control flow instructions. + */ + struct DxbcConditional { + uint32_t labelIf = 0; + uint32_t labelElse = 0; + uint32_t labelEnd = 0; + }; + + /** * \brief Vertex shader-specific structure */ @@ -120,6 +148,7 @@ namespace dxvk { uint32_t builtinLayer = 0; uint32_t builtinViewportId = 0; + uint32_t builtinInvocationId = 0; }; @@ -136,6 +165,8 @@ namespace dxvk { uint32_t builtinSampleMaskIn = 0; uint32_t builtinSampleMaskOut = 0; uint32_t builtinLayer = 0; + + uint32_t killState = 0; }; @@ -244,10 +275,11 @@ namespace dxvk { struct DxbcCfgBlockIf { + uint32_t ztestId; uint32_t labelIf; uint32_t labelElse; uint32_t labelEnd; - bool hadElse; + size_t headerPtr; }; @@ -382,6 +414,13 @@ namespace dxvk { // currently active if-else blocks and loops. std::vector 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. @@ -513,6 +552,9 @@ namespace dxvk { void emitDclThreadGroup( const DxbcShaderInstruction& ins); + void emitDclGsInstanceCount( + const DxbcShaderInstruction& ins); + uint32_t emitDclUavCounter( uint32_t regId); @@ -843,6 +885,14 @@ namespace dxvk { const DxbcRegister& reg, DxbcRegisterValue value); + //////////////////////////////////////// + // Spec constant declaration and access + DxbcRegisterValue getSpecConstant( + DxvkSpecConstantId specId); + + DxbcSpecConstant getSpecConstantProperties( + DxvkSpecConstantId specId); + //////////////////////////// // Input/output preparation void emitInputSetup(); @@ -906,6 +956,11 @@ namespace dxvk { DxbcSystemValue sv, uint32_t srcArray); + /////////////////////////////// + // Some state checking methods + uint32_t emitUavWriteTest( + const DxbcBufferInfo& uav); + ////////////////////////////////////// // Common function definition methods void emitInit(); 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; }; 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/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(); diff --git a/src/dxbc/dxbc_options.cpp b/src/dxbc/dxbc_options.cpp index 22b925b7..4a1621b8 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) }, + }}; @@ -36,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 abe29521..72bedd56 100644 --- a/src/dxbc/dxbc_options.h +++ b/src/dxbc/dxbc_options.h @@ -18,10 +18,10 @@ namespace dxvk { /// Workaround for bugs in older Nvidia drivers. UseSimpleMinMaxClamp, - /// Enforces the use of array views even when dealing - /// with non-array texture types. Some games do not - /// bind the correct texture type to the pipeline. - ForceTex2DArray, + /// Defer kill operation to the end of the shader. + /// Fixes derivatives that are undefined due to + /// non-uniform control flow in fragment shaders. + DeferKill, }; using DxbcOptions = Flags; diff --git a/src/dxgi/dxgi_adapter.cpp b/src/dxgi/dxgi_adapter.cpp index 8bf91fc3..8c43a2bf 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(); @@ -158,20 +181,22 @@ namespace dxvk { #ifndef _WIN64 // The value returned by DXGI is a 32-bit value // on 32-bit platforms, so we need to clamp it - VkDeviceSize maxMemory = 0xF0000000; + VkDeviceSize maxMemory = 0xC0000000; deviceMemory = std::min(deviceMemory, maxMemory); sharedMemory = std::min(sharedMemory, maxMemory); #endif - pDesc->VendorId = deviceProp.vendorID; - pDesc->DeviceId = deviceProp.deviceID; - pDesc->SubSysId = 0; - pDesc->Revision = 0; - pDesc->DedicatedVideoMemory = deviceMemory; - pDesc->DedicatedSystemMemory = 0; - pDesc->SharedSystemMemory = sharedMemory; - pDesc->AdapterLuid = LUID { 0, 0 }; // TODO implement - pDesc->Flags = 0; + pDesc->VendorId = deviceProp.vendorID; + pDesc->DeviceId = deviceProp.deviceID; + pDesc->SubSysId = 0; + pDesc->Revision = 0; + pDesc->DedicatedVideoMemory = deviceMemory; + pDesc->DedicatedSystemMemory = 0; + pDesc->SharedSystemMemory = sharedMemory; + pDesc->AdapterLuid = LUID { 0, 0 }; // TODO implement + pDesc->Flags = 0; + pDesc->GraphicsPreemptionGranularity = DXGI_GRAPHICS_PREEMPTION_DMA_BUFFER_BOUNDARY; + pDesc->ComputePreemptionGranularity = DXGI_COMPUTE_PREEMPTION_DMA_BUFFER_BOUNDARY; return S_OK; } 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_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; } diff --git a/src/dxgi/dxgi_factory.cpp b/src/dxgi/dxgi_factory.cpp index d1638d6d..dcffe6f9 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) { @@ -58,16 +65,67 @@ namespace dxvk { IUnknown* pDevice, DXGI_SWAP_CHAIN_DESC* pDesc, IDXGISwapChain** ppSwapChain) { + if (ppSwapChain == nullptr || pDesc == nullptr || pDevice == nullptr) + return DXGI_ERROR_INVALID_CALL; + + DXGI_SWAP_CHAIN_DESC1 desc; + desc.Width = pDesc->BufferDesc.Width; + desc.Height = pDesc->BufferDesc.Height; + desc.Format = pDesc->BufferDesc.Format; + desc.Stereo = FALSE; + desc.SampleDesc = pDesc->SampleDesc; + desc.BufferUsage = pDesc->BufferUsage; + desc.BufferCount = pDesc->BufferCount; + desc.Scaling = DXGI_SCALING_STRETCH; + desc.SwapEffect = pDesc->SwapEffect; + desc.AlphaMode = DXGI_ALPHA_MODE_IGNORE; + desc.Flags = pDesc->Flags; + + DXGI_SWAP_CHAIN_FULLSCREEN_DESC descFs; + descFs.RefreshRate = pDesc->BufferDesc.RefreshRate; + descFs.ScanlineOrdering = pDesc->BufferDesc.ScanlineOrdering; + descFs.Scaling = pDesc->BufferDesc.Scaling; + descFs.Windowed = pDesc->Windowed; + + IDXGISwapChain1* swapChain = nullptr; + HRESULT hr = CreateSwapChainForHwnd( + pDevice, pDesc->OutputWindow, + &desc, &descFs, nullptr, + &swapChain); + + *ppSwapChain = swapChain; + return hr; + } + + + HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForHwnd( + IUnknown* pDevice, + HWND hWnd, + const DXGI_SWAP_CHAIN_DESC1* pDesc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc, + IDXGIOutput* pRestrictToOutput, + IDXGISwapChain1** ppSwapChain) { InitReturnPtr(ppSwapChain); - if (ppSwapChain == nullptr || pDesc == nullptr || pDevice == NULL) + if (ppSwapChain == nullptr || pDesc == nullptr || hWnd == nullptr || pDevice == nullptr) return DXGI_ERROR_INVALID_CALL; - if (pDesc->OutputWindow == nullptr) - return DXGI_ERROR_INVALID_CALL; + // If necessary, set up a default set of + // fullscreen parameters for the swap chain + DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreenDesc; + + if (pFullscreenDesc != nullptr) { + fullscreenDesc = *pFullscreenDesc; + } else { + fullscreenDesc.RefreshRate = { 0, 0 }; + fullscreenDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; + fullscreenDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; + fullscreenDesc.Windowed = TRUE; + } try { - *ppSwapChain = ref(new DxgiSwapChain(this, pDevice, pDesc)); + *ppSwapChain = ref(new DxgiSwapChain(this, + pDevice, hWnd, pDesc, &fullscreenDesc)); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); @@ -76,6 +134,31 @@ namespace dxvk { } + HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForCoreWindow( + IUnknown* pDevice, + IUnknown* pWindow, + const DXGI_SWAP_CHAIN_DESC1* pDesc, + IDXGIOutput* pRestrictToOutput, + IDXGISwapChain1** ppSwapChain) { + InitReturnPtr(ppSwapChain); + + Logger::err("DxgiFactory::CreateSwapChainForCoreWindow: Not implemented"); + return E_NOTIMPL; + } + + + HRESULT STDMETHODCALLTYPE DxgiFactory::CreateSwapChainForComposition( + IUnknown* pDevice, + const DXGI_SWAP_CHAIN_DESC1* pDesc, + IDXGIOutput* pRestrictToOutput, + IDXGISwapChain1** ppSwapChain) { + InitReturnPtr(ppSwapChain); + + Logger::err("DxgiFactory::CreateSwapChainForComposition: Not implemented"); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE DxgiFactory::EnumAdapters( UINT Adapter, IDXGIAdapter** ppAdapter) { @@ -117,6 +200,14 @@ namespace dxvk { } + HRESULT STDMETHODCALLTYPE DxgiFactory::GetSharedResourceAdapterLuid( + HANDLE hResource, + LUID* pLuid) { + Logger::err("DxgiFactory::GetSharedResourceAdapterLuid: Not implemented"); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE DxgiFactory::MakeWindowAssociation(HWND WindowHandle, UINT Flags) { Logger::warn("DXGI: MakeWindowAssociation: Ignoring flags"); m_associatedWindow = WindowHandle; @@ -129,4 +220,50 @@ namespace dxvk { return TRUE; } + + HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterOcclusionStatusWindow( + HWND WindowHandle, + UINT wMsg, + DWORD* pdwCookie) { + Logger::err("DxgiFactory::RegisterOcclusionStatusWindow: Not implemented"); + return E_NOTIMPL; + } + + + HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterStereoStatusEvent( + HANDLE hEvent, + DWORD* pdwCookie) { + Logger::err("DxgiFactory::RegisterStereoStatusEvent: Not implemented"); + return E_NOTIMPL; + } + + + HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterStereoStatusWindow( + HWND WindowHandle, + UINT wMsg, + DWORD* pdwCookie) { + Logger::err("DxgiFactory::RegisterStereoStatusWindow: Not implemented"); + return E_NOTIMPL; + } + + + HRESULT STDMETHODCALLTYPE DxgiFactory::RegisterOcclusionStatusEvent( + HANDLE hEvent, + DWORD* pdwCookie) { + Logger::err("DxgiFactory::RegisterOcclusionStatusEvent: Not implemented"); + return E_NOTIMPL; + } + + + void STDMETHODCALLTYPE DxgiFactory::UnregisterStereoStatus( + DWORD dwCookie) { + Logger::err("DxgiFactory::UnregisterStereoStatus: Not implemented"); + } + + + void STDMETHODCALLTYPE DxgiFactory::UnregisterOcclusionStatus( + DWORD dwCookie) { + Logger::err("DxgiFactory::UnregisterOcclusionStatus: Not implemented"); + } + } 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: diff --git a/src/dxgi/dxgi_interfaces.h b/src/dxgi/dxgi_interfaces.h index 7f76f257..d46f98d9 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; @@ -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_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; 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/dxgi_presenter.cpp b/src/dxgi/dxgi_presenter.cpp index 116ea212..f041ab6a 100644 --- a/src/dxgi/dxgi_presenter.cpp +++ b/src/dxgi/dxgi_presenter.cpp @@ -10,14 +10,16 @@ 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)); + // Some games don't work with deferred surface creation, + // so we should default to initializing it immediately. + DxgiOptions dxgiOptions = getDxgiAppOptions(env::getExeName()); - m_surface = m_device->adapter()->createSurface(instance, window); + if (!dxgiOptions.test(DxgiOption::DeferSurfaceCreation)) + m_surface = CreateSurface(); // Reset options for the swap chain itself. We will // create a swap chain object before presentation. @@ -275,27 +277,52 @@ namespace dxvk { } - void DxgiVkPresenter::RecreateSwapchain(const DxvkSwapchainProperties* pOptions) { + void DxgiVkPresenter::SetGammaControl( + const DXGI_VK_GAMMA_CURVE* pGammaCurve) { + m_context->beginRecording( + m_device->createCommandList()); + + m_context->updateImage(m_gammaTexture, + VkImageSubresourceLayers { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1 }, + VkOffset3D { 0, 0, 0 }, + VkExtent3D { DXGI_VK_GAMMA_CP_COUNT, 1, 1 }, + pGammaCurve, 0, 0); + + m_device->submitCommandList( + m_context->endRecording(), + nullptr, nullptr); + } + + + void DxgiVkPresenter::RecreateSwapchain(DXGI_FORMAT Format, VkPresentModeKHR PresentMode, VkExtent2D WindowSize) { + if (m_surface == nullptr) + m_surface = CreateSurface(); + + DxvkSwapchainProperties options; + options.preferredSurfaceFormat = PickSurfaceFormat(Format); + options.preferredPresentMode = PickPresentMode(PresentMode); + options.preferredBufferSize = WindowSize; + const bool doRecreate = - pOptions->preferredSurfaceFormat.format != m_options.preferredSurfaceFormat.format - || pOptions->preferredSurfaceFormat.colorSpace != m_options.preferredSurfaceFormat.colorSpace - || pOptions->preferredPresentMode != m_options.preferredPresentMode - || pOptions->preferredBufferSize.width != m_options.preferredBufferSize.width - || pOptions->preferredBufferSize.height != m_options.preferredBufferSize.height; + options.preferredSurfaceFormat.format != m_options.preferredSurfaceFormat.format + || options.preferredSurfaceFormat.colorSpace != m_options.preferredSurfaceFormat.colorSpace + || options.preferredPresentMode != m_options.preferredPresentMode + || options.preferredBufferSize.width != m_options.preferredBufferSize.width + || options.preferredBufferSize.height != m_options.preferredBufferSize.height; if (doRecreate) { Logger::info(str::format( "DxgiVkPresenter: Recreating swap chain: ", - "\n Format: ", pOptions->preferredSurfaceFormat.format, - "\n Present mode: ", pOptions->preferredPresentMode, - "\n Buffer size: ", pOptions->preferredBufferSize.width, "x", pOptions->preferredBufferSize.height)); + "\n Format: ", options.preferredSurfaceFormat.format, + "\n Present mode: ", options.preferredPresentMode, + "\n Buffer size: ", options.preferredBufferSize.width, "x", options.preferredBufferSize.height)); if (m_swapchain == nullptr) - m_swapchain = m_device->createSwapchain(m_surface, *pOptions); + m_swapchain = m_device->createSwapchain(m_surface, options); else - m_swapchain->changeProperties(*pOptions); + m_swapchain->changeProperties(options); - m_options = *pOptions; + m_options = options; } } @@ -339,20 +366,11 @@ namespace dxvk { } - void DxgiVkPresenter::SetGammaControl( - const DXGI_VK_GAMMA_CURVE* pGammaCurve) { - m_context->beginRecording( - m_device->createCommandList()); + Rc 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..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 { @@ -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 0fcded51..343cbc04 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())) @@ -73,7 +76,8 @@ namespace dxvk { if (riid == __uuidof(IUnknown) || riid == __uuidof(IDXGIObject) || riid == __uuidof(IDXGIDeviceSubObject) - || riid == __uuidof(IDXGISwapChain)) { + || riid == __uuidof(IDXGISwapChain) + || riid == __uuidof(IDXGISwapChain1)) { *ppvObject = ref(this); return S_OK; } @@ -99,7 +103,7 @@ namespace dxvk { std::lock_guard lock(m_mutex); - if (!IsWindow(m_desc.OutputWindow)) + if (!IsWindow(m_window)) return DXGI_ERROR_INVALID_CALL; if (Buffer > 0) { @@ -116,11 +120,11 @@ namespace dxvk { std::lock_guard lock(m_mutex); - if (!IsWindow(m_desc.OutputWindow)) + if (!IsWindow(m_window)) return DXGI_ERROR_INVALID_CALL; RECT windowRect = { 0, 0, 0, 0 }; - ::GetWindowRect(m_desc.OutputWindow, &windowRect); + ::GetWindowRect(m_window, &windowRect); HMONITOR monitor = ::MonitorFromPoint( { (windowRect.left + windowRect.right) / 2, @@ -134,6 +138,29 @@ namespace dxvk { HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDesc(DXGI_SWAP_CHAIN_DESC* pDesc) { std::lock_guard lock(m_mutex); + if (pDesc == nullptr) + return DXGI_ERROR_INVALID_CALL; + + pDesc->BufferDesc.Width = m_desc.Width; + pDesc->BufferDesc.Height = m_desc.Height; + pDesc->BufferDesc.RefreshRate = m_descFs.RefreshRate; + pDesc->BufferDesc.Format = m_desc.Format; + pDesc->BufferDesc.ScanlineOrdering = m_descFs.ScanlineOrdering; + pDesc->BufferDesc.Scaling = m_descFs.Scaling; + pDesc->SampleDesc = m_desc.SampleDesc; + pDesc->BufferUsage = m_desc.BufferUsage; + pDesc->BufferCount = m_desc.BufferCount; + pDesc->OutputWindow = m_window; + pDesc->Windowed = m_descFs.Windowed; + pDesc->SwapEffect = m_desc.SwapEffect; + pDesc->Flags = m_desc.Flags; + return S_OK; + } + + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetDesc1(DXGI_SWAP_CHAIN_DESC1* pDesc) { + std::lock_guard lock(m_mutex); + if (pDesc == nullptr) return DXGI_ERROR_INVALID_CALL; @@ -142,6 +169,29 @@ namespace dxvk { } + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetBackgroundColor( + DXGI_RGBA* pColor) { + Logger::err("DxgiSwapChain::GetBackgroundColor: Not implemented"); + return E_NOTIMPL; + } + + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetRotation( + DXGI_MODE_ROTATION* pRotation) { + Logger::err("DxgiSwapChain::GetRotation: Not implemented"); + return E_NOTIMPL; + } + + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetRestrictToOutput( + IDXGIOutput** ppRestrictToOutput) { + InitReturnPtr(ppRestrictToOutput); + + Logger::err("DxgiSwapChain::GetRestrictToOutput: Not implemented"); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFrameStatistics(DXGI_FRAME_STATISTICS* pStats) { std::lock_guard lock(m_mutex); @@ -158,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); } @@ -177,6 +227,38 @@ namespace dxvk { } + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetFullscreenDesc( + DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) { + 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) { + if (pHwnd == nullptr) + return DXGI_ERROR_INVALID_CALL; + + *pHwnd = m_window; + return S_OK; + } + + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetCoreWindow( + REFIID refiid, + void** ppUnk) { + InitReturnPtr(ppUnk); + + Logger::err("DxgiSwapChain::GetCoreWindow: Not implemented"); + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::GetLastPresentCount(UINT* pLastPresentCount) { std::lock_guard lock(m_mutex); @@ -188,10 +270,17 @@ namespace dxvk { } + BOOL STDMETHODCALLTYPE DxgiSwapChain::IsTemporaryMonoSupported() { + // This seems to be related to stereo 3D display + // modes, which we don't support at the moment + return FALSE; + } + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present(UINT SyncInterval, UINT Flags) { std::lock_guard lock(m_mutex); - if (!IsWindow(m_desc.OutputWindow)) + if (!IsWindow(m_window)) return DXGI_ERROR_INVALID_CALL; if (Flags & DXGI_PRESENT_TEST) @@ -217,15 +306,11 @@ namespace dxvk { // up vertical synchronization properly, but also apply // changes that were made to the window size even if the // Vulkan swap chain itself remains valid. - DxvkSwapchainProperties swapchainProps; - swapchainProps.preferredSurfaceFormat - = m_presenter->PickSurfaceFormat(m_desc.BufferDesc.Format); - swapchainProps.preferredPresentMode = SyncInterval == 0 - ? m_presenter->PickPresentMode(VK_PRESENT_MODE_IMMEDIATE_KHR) - : m_presenter->PickPresentMode(VK_PRESENT_MODE_FIFO_KHR); - swapchainProps.preferredBufferSize = GetWindowSize(); - - m_presenter->RecreateSwapchain(&swapchainProps); + VkPresentModeKHR presentMode = SyncInterval == 0 + ? VK_PRESENT_MODE_IMMEDIATE_KHR + : VK_PRESENT_MODE_FIFO_KHR; + + m_presenter->RecreateSwapchain(m_desc.Format, presentMode, GetWindowSize()); m_presenter->PresentImage(); return S_OK; } catch (const DxvkError& err) { @@ -235,6 +320,17 @@ namespace dxvk { } + HRESULT STDMETHODCALLTYPE DxgiSwapChain::Present1( + UINT SyncInterval, + UINT PresentFlags, + const DXGI_PRESENT_PARAMETERS* pPresentParameters) { + if (pPresentParameters != nullptr) + Logger::warn("DXGI: Present parameters not supported"); + + return Present(SyncInterval, PresentFlags); + } + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::ResizeBuffers( UINT BufferCount, UINT Width, @@ -243,19 +339,19 @@ namespace dxvk { UINT SwapChainFlags) { std::lock_guard lock(m_mutex); - if (!IsWindow(m_desc.OutputWindow)) + if (!IsWindow(m_window)) return DXGI_ERROR_INVALID_CALL; const VkExtent2D windowSize = GetWindowSize(); - m_desc.BufferDesc.Width = Width != 0 ? Width : windowSize.width; - m_desc.BufferDesc.Height = Height != 0 ? Height : windowSize.height; + m_desc.Width = Width != 0 ? Width : windowSize.width; + m_desc.Height = Height != 0 ? Height : windowSize.height; if (BufferCount != 0) m_desc.BufferCount = BufferCount; if (NewFormat != DXGI_FORMAT_UNKNOWN) - m_desc.BufferDesc.Format = NewFormat; + m_desc.Format = NewFormat; return CreateBackBuffer(); } @@ -267,29 +363,29 @@ namespace dxvk { if (pNewTargetParameters == nullptr) return DXGI_ERROR_INVALID_CALL; - if (!IsWindow(m_desc.OutputWindow)) + if (!IsWindow(m_window)) return DXGI_ERROR_INVALID_CALL; // Update the swap chain description if (pNewTargetParameters->RefreshRate.Numerator != 0) - m_desc.BufferDesc.RefreshRate = pNewTargetParameters->RefreshRate; + m_descFs.RefreshRate = pNewTargetParameters->RefreshRate; - m_desc.BufferDesc.ScanlineOrdering = pNewTargetParameters->ScanlineOrdering; - m_desc.BufferDesc.Scaling = pNewTargetParameters->Scaling; + m_descFs.ScanlineOrdering = pNewTargetParameters->ScanlineOrdering; + m_descFs.Scaling = pNewTargetParameters->Scaling; - if (m_desc.Windowed) { + if (m_descFs.Windowed) { // Adjust window position and size RECT newRect = { 0, 0, 0, 0 }; RECT oldRect = { 0, 0, 0, 0 }; - ::GetWindowRect(m_desc.OutputWindow, &oldRect); + ::GetWindowRect(m_window, &oldRect); ::SetRect(&newRect, 0, 0, pNewTargetParameters->Width, pNewTargetParameters->Height); ::AdjustWindowRectEx(&newRect, - ::GetWindowLongW(m_desc.OutputWindow, GWL_STYLE), FALSE, - ::GetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE)); + ::GetWindowLongW(m_window, GWL_STYLE), FALSE, + ::GetWindowLongW(m_window, GWL_EXSTYLE)); ::SetRect(&newRect, 0, 0, newRect.right - newRect.left, newRect.bottom - newRect.top); ::OffsetRect(&newRect, oldRect.left, oldRect.top); - ::MoveWindow(m_desc.OutputWindow, newRect.left, newRect.top, + ::MoveWindow(m_window, newRect.left, newRect.top, newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE); } else { Com output; @@ -309,7 +405,7 @@ namespace dxvk { const RECT newRect = desc.DesktopCoordinates; - ::MoveWindow(m_desc.OutputWindow, newRect.left, newRect.top, + ::MoveWindow(m_window, newRect.left, newRect.top, newRect.right - newRect.left, newRect.bottom - newRect.top, TRUE); } @@ -323,18 +419,32 @@ namespace dxvk { IDXGIOutput* pTarget) { std::lock_guard lock(m_mutex); - if (!IsWindow(m_desc.OutputWindow)) + if (!IsWindow(m_window)) return DXGI_ERROR_INVALID_CALL; - if (m_desc.Windowed && Fullscreen) + if (m_descFs.Windowed && Fullscreen) return this->EnterFullscreenMode(pTarget); - else if (!m_desc.Windowed && !Fullscreen) + else if (!m_descFs.Windowed && !Fullscreen) return this->LeaveFullscreenMode(); return S_OK; } + HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetBackgroundColor( + const DXGI_RGBA* pColor) { + Logger::err("DxgiSwapChain::SetBackgroundColor: Not implemented"); + return E_NOTIMPL; + } + + + HRESULT STDMETHODCALLTYPE DxgiSwapChain::SetRotation( + DXGI_MODE_ROTATION Rotation) { + Logger::err("DxgiSwapChain::SetRotation: Not implemented"); + return E_NOTIMPL; + } + + HRESULT DxgiSwapChain::SetGammaControl(const DXGI_GAMMA_CONTROL* pGammaControl) { std::lock_guard lock(m_mutex); @@ -373,7 +483,7 @@ namespace dxvk { try { m_presenter = new DxgiVkPresenter( m_device->GetDXVKDevice(), - m_desc.OutputWindow); + m_window); return S_OK; } catch (const DxvkError& e) { Logger::err(e.message()); @@ -412,7 +522,7 @@ namespace dxvk { VkExtent2D DxgiSwapChain::GetWindowSize() const { RECT windowRect; - if (!::GetClientRect(m_desc.OutputWindow, &windowRect)) + if (!::GetClientRect(m_window, &windowRect)) windowRect = RECT(); VkExtent2D result; @@ -433,14 +543,18 @@ namespace dxvk { } // Find a display mode that matches what we need - ::GetWindowRect(m_desc.OutputWindow, &m_windowState.rect); + ::GetWindowRect(m_window, &m_windowState.rect); if (m_desc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH) { auto windowRect = m_windowState.rect; - DXGI_MODE_DESC displayMode = m_desc.BufferDesc; - displayMode.Width = windowRect.right - windowRect.left; - displayMode.Height = windowRect.bottom - windowRect.top; + DXGI_MODE_DESC displayMode; + displayMode.Width = windowRect.right - windowRect.left; + displayMode.Height = windowRect.bottom - windowRect.top; + displayMode.RefreshRate = m_descFs.RefreshRate; + displayMode.Format = m_desc.Format; + displayMode.ScanlineOrdering = m_descFs.ScanlineOrdering; + displayMode.Scaling = m_descFs.Scaling; if (FAILED(ChangeDisplayMode(output.ptr(), &displayMode))) { Logger::err("DXGI: EnterFullscreenMode: Failed to change display mode"); @@ -449,11 +563,11 @@ namespace dxvk { } // Update swap chain description - m_desc.Windowed = FALSE; + m_descFs.Windowed = FALSE; // Change the window flags to remove the decoration etc. - LONG style = ::GetWindowLongW(m_desc.OutputWindow, GWL_STYLE); - LONG exstyle = ::GetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE); + LONG style = ::GetWindowLongW(m_window, GWL_STYLE); + LONG exstyle = ::GetWindowLongW(m_window, GWL_EXSTYLE); m_windowState.style = style; m_windowState.exstyle = exstyle; @@ -461,8 +575,8 @@ namespace dxvk { style &= ~WS_OVERLAPPEDWINDOW; exstyle &= ~WS_EX_OVERLAPPEDWINDOW; - ::SetWindowLongW(m_desc.OutputWindow, GWL_STYLE, style); - ::SetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE, exstyle); + ::SetWindowLongW(m_window, GWL_STYLE, style); + ::SetWindowLongW(m_window, GWL_EXSTYLE, exstyle); // Move the window so that it covers the entire output DXGI_OUTPUT_DESC desc; @@ -470,7 +584,7 @@ namespace dxvk { const RECT rect = desc.DesktopCoordinates; - ::SetWindowPos(m_desc.OutputWindow, HWND_TOPMOST, + ::SetWindowPos(m_window, HWND_TOPMOST, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOACTIVATE); @@ -487,24 +601,24 @@ namespace dxvk { Logger::warn("DXGI: LeaveFullscreenMode: Failed to restore display mode"); // Restore internal state - m_desc.Windowed = TRUE; + m_descFs.Windowed = TRUE; m_monitor = nullptr; // Only restore the window style if the application hasn't // changed them. This is in line with what native DXGI does. - LONG curStyle = ::GetWindowLongW(m_desc.OutputWindow, GWL_STYLE) & ~WS_VISIBLE; - LONG curExstyle = ::GetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE) & ~WS_EX_TOPMOST; + LONG curStyle = ::GetWindowLongW(m_window, GWL_STYLE) & ~WS_VISIBLE; + LONG curExstyle = ::GetWindowLongW(m_window, GWL_EXSTYLE) & ~WS_EX_TOPMOST; if (curStyle == (m_windowState.style & ~(WS_VISIBLE | WS_OVERLAPPEDWINDOW)) && curExstyle == (m_windowState.exstyle & ~(WS_EX_TOPMOST | WS_EX_OVERLAPPEDWINDOW))) { - ::SetWindowLongW(m_desc.OutputWindow, GWL_STYLE, m_windowState.style); - ::SetWindowLongW(m_desc.OutputWindow, GWL_EXSTYLE, m_windowState.exstyle); + ::SetWindowLongW(m_window, GWL_STYLE, m_windowState.style); + ::SetWindowLongW(m_window, GWL_EXSTYLE, m_windowState.exstyle); } // Restore window position and apply the style const RECT rect = m_windowState.rect; - ::SetWindowPos(m_desc.OutputWindow, 0, + ::SetWindowPos(m_window, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOACTIVATE); diff --git a/src/dxgi/dxgi_swapchain.h b/src/dxgi/dxgi_swapchain.h index b6c75c84..188c1c88 100644 --- a/src/dxgi/dxgi_swapchain.h +++ b/src/dxgi/dxgi_swapchain.h @@ -20,69 +20,107 @@ 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* pFactory, + IUnknown* pDevice, + HWND hWnd, + const DXGI_SWAP_CHAIN_DESC1* pDesc, + const DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pFullscreenDesc); + ~DxgiSwapChain(); HRESULT STDMETHODCALLTYPE QueryInterface( - REFIID riid, - void** ppvObject) final; + REFIID riid, + void** ppvObject) final; HRESULT STDMETHODCALLTYPE GetParent( - REFIID riid, - void** ppParent) final; + REFIID riid, + void** ppParent) final; HRESULT STDMETHODCALLTYPE GetDevice( - REFIID riid, - void** ppDevice) final; + REFIID riid, + void** ppDevice) final; HRESULT STDMETHODCALLTYPE GetBuffer( - UINT Buffer, - REFIID riid, - void** ppSurface) final; + UINT Buffer, + REFIID riid, + void** ppSurface) final; HRESULT STDMETHODCALLTYPE GetContainingOutput( - IDXGIOutput** ppOutput) final; + IDXGIOutput** ppOutput) final; HRESULT STDMETHODCALLTYPE GetDesc( - DXGI_SWAP_CHAIN_DESC* pDesc) final; + DXGI_SWAP_CHAIN_DESC* pDesc) final; - HRESULT STDMETHODCALLTYPE GetFrameStatistics( - DXGI_FRAME_STATISTICS* pStats) final; + HRESULT STDMETHODCALLTYPE GetDesc1( + DXGI_SWAP_CHAIN_DESC1* pDesc) final; HRESULT STDMETHODCALLTYPE GetFullscreenState( - BOOL* pFullscreen, - IDXGIOutput** ppTarget) final; + BOOL* pFullscreen, + IDXGIOutput** ppTarget) final; + + HRESULT STDMETHODCALLTYPE GetFullscreenDesc( + DXGI_SWAP_CHAIN_FULLSCREEN_DESC* pDesc) final; + + HRESULT STDMETHODCALLTYPE GetHwnd( + HWND* pHwnd) final; + + HRESULT STDMETHODCALLTYPE GetCoreWindow( + REFIID refiid, + void** ppUnk) final; + + HRESULT STDMETHODCALLTYPE GetBackgroundColor( + DXGI_RGBA* pColor) final; + + HRESULT STDMETHODCALLTYPE GetRotation( + DXGI_MODE_ROTATION* pRotation) final; + + HRESULT STDMETHODCALLTYPE GetRestrictToOutput( + IDXGIOutput** ppRestrictToOutput) final; + + HRESULT STDMETHODCALLTYPE GetFrameStatistics( + DXGI_FRAME_STATISTICS* pStats) final; HRESULT STDMETHODCALLTYPE GetLastPresentCount( - UINT* pLastPresentCount) final; + UINT* pLastPresentCount) final; + BOOL STDMETHODCALLTYPE IsTemporaryMonoSupported() final; + HRESULT STDMETHODCALLTYPE Present( - UINT SyncInterval, - UINT Flags) final; + UINT SyncInterval, + UINT Flags) final; + HRESULT STDMETHODCALLTYPE Present1( + UINT SyncInterval, + UINT PresentFlags, + const DXGI_PRESENT_PARAMETERS* pPresentParameters) final; + HRESULT STDMETHODCALLTYPE ResizeBuffers( - UINT BufferCount, - UINT Width, - UINT Height, - DXGI_FORMAT NewFormat, - UINT SwapChainFlags) final; + UINT BufferCount, + UINT Width, + UINT Height, + DXGI_FORMAT NewFormat, + UINT SwapChainFlags) final; HRESULT STDMETHODCALLTYPE ResizeTarget( - const DXGI_MODE_DESC* pNewTargetParameters) final; + const DXGI_MODE_DESC* pNewTargetParameters) final; HRESULT STDMETHODCALLTYPE SetFullscreenState( - BOOL Fullscreen, - IDXGIOutput* pTarget) final; + BOOL Fullscreen, + IDXGIOutput* pTarget) final; + HRESULT STDMETHODCALLTYPE SetBackgroundColor( + const DXGI_RGBA* pColor) final; + + HRESULT STDMETHODCALLTYPE SetRotation( + DXGI_MODE_ROTATION Rotation) final; + HRESULT SetGammaControl( - const DXGI_GAMMA_CONTROL* pGammaControl); + const DXGI_GAMMA_CONTROL* pGammaControl); HRESULT SetDefaultGammaControl(); @@ -101,7 +139,9 @@ namespace dxvk { Com 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; 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', 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_context.cpp b/src/dxvk/dxvk_context.cpp index bb966228..87205d0f 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() { @@ -228,6 +230,9 @@ namespace dxvk { uint32_t value) { this->spillRenderPass(); + if (length == buffer->info().size) + length = align(length, 4); + auto slice = buffer->subSlice(offset, length); m_cmd->cmdFillBuffer( @@ -976,123 +981,102 @@ namespace dxvk { void DxvkContext::generateMipmaps( - const Rc& 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->image()); } @@ -1399,15 +1383,19 @@ namespace dxvk { } } - m_cmd->cmdSetViewport(0, viewportCount, m_state.vp.viewports.data()); - m_cmd->cmdSetScissor (0, viewportCount, m_state.vp.scissorRects.data()); + if (m_gpActivePipeline != VK_NULL_HANDLE) { + m_cmd->cmdSetViewport(0, viewportCount, m_state.vp.viewports.data()); + m_cmd->cmdSetScissor (0, viewportCount, m_state.vp.scissorRects.data()); + } } void DxvkContext::setBlendConstants( const DxvkBlendConstants& blendConstants) { m_state.om.blendConstants = blendConstants; - m_cmd->cmdSetBlendConstants(&blendConstants.r); + + if (m_gpActivePipeline != VK_NULL_HANDLE) + m_cmd->cmdSetBlendConstants(&blendConstants.r); } @@ -1415,9 +1403,11 @@ namespace dxvk { const uint32_t reference) { m_state.om.stencilReference = reference; - m_cmd->cmdSetStencilReference( - VK_STENCIL_FRONT_AND_BACK, - reference); + if (m_gpActivePipeline != VK_NULL_HANDLE) { + m_cmd->cmdSetStencilReference( + VK_STENCIL_FRONT_AND_BACK, + reference); + } } @@ -1680,6 +1670,18 @@ namespace dxvk { } + void DxvkContext::unbindGraphicsPipeline() { + m_flags.set( + DxvkContextFlag::GpDirtyPipeline, + DxvkContextFlag::GpDirtyPipelineState, + DxvkContextFlag::GpDirtyResources, + DxvkContextFlag::GpDirtyVertexBuffers, + DxvkContextFlag::GpDirtyIndexBuffer); + + m_gpActivePipeline = VK_NULL_HANDLE; + } + + void DxvkContext::updateGraphicsPipeline() { if (m_flags.test(DxvkContextFlag::GpDirtyPipeline)) { m_flags.clr(DxvkContextFlag::GpDirtyPipeline); @@ -1822,11 +1824,11 @@ namespace dxvk { case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE: case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE: - if (res.imageView != nullptr && res.imageView->type() == binding.view) { + if (res.imageView != nullptr && res.imageView->handle(binding.view) != VK_NULL_HANDLE) { updatePipelineState |= bindingState.setBound(i); m_descInfos[i].image.sampler = VK_NULL_HANDLE; - m_descInfos[i].image.imageView = res.imageView->handle(); + m_descInfos[i].image.imageView = res.imageView->handle(binding.view); m_descInfos[i].image.imageLayout = res.imageView->imageInfo().layout; if (depthAttachment.view != nullptr 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; 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; 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 { 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..afbb574e 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 { @@ -272,6 +285,14 @@ namespace dxvk { return m_info; } + /** + * \brief Image handle + * \returns Image handle + */ + VkImage imageHandle() const { + return m_image->handle(); + } + /** * \brief Image properties * \returns Image properties @@ -289,8 +310,8 @@ namespace dxvk { } /** - * \brief Image - * \returns Image + * \brief Image object + * \returns Image object */ Rc image() const { return m_image; @@ -336,7 +357,9 @@ namespace dxvk { Rc m_image; DxvkImageViewCreateInfo m_info; - VkImageView m_view; + VkImageView m_views[ViewCount]; + + void createView(VkImageViewType type, uint32_t numLayers); }; 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/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")); 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..ab4ade66 --- /dev/null +++ b/src/dxvk/dxvk_spec_const.cpp @@ -0,0 +1,41 @@ +#include "dxvk_spec_const.h" + +#define SET_CONSTANT_ENTRY(specId, member) \ + this->setConstantEntry(specId, \ + offsetof(DxvkSpecConstantData, member), \ + sizeof(DxvkSpecConstantData::member)) + +namespace dxvk { + + DxvkSpecConstantMap g_specConstantMap; + + DxvkSpecConstantMap::DxvkSpecConstantMap() { + SET_CONSTANT_ENTRY(DxvkSpecConstantId::RasterizerSampleCount, rasterizerSampleCount); + + for (uint32_t i = 0; i < MaxNumActiveBindings; i++) + this->setBindingEntry(i); + } + + + void DxvkSpecConstantMap::setConstantEntry( + DxvkSpecConstantId specId, + uint32_t offset, + uint32_t size) { + VkSpecializationMapEntry entry; + entry.constantID = uint32_t(specId); + entry.offset = offset; + entry.size = size; + m_mapEntries[uint32_t(specId) - uint32_t(DxvkSpecConstantId::SpecConstantIdMin)] = entry; + } + + + void DxvkSpecConstantMap::setBindingEntry( + uint32_t binding) { + VkSpecializationMapEntry entry; + entry.constantID = binding; + entry.offset = sizeof(VkBool32) * binding + offsetof(DxvkSpecConstantData, activeBindings); + entry.size = sizeof(VkBool32); + m_mapEntries[MaxNumSpecConstants + binding] = entry; + } + +} \ 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 02bf5cf9..e7bc8cc8 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_openvr.cpp', 'dxvk_pipecache.cpp', @@ -55,6 +62,7 @@ dxvk_src = files([ 'dxvk_resource.cpp', 'dxvk_sampler.cpp', 'dxvk_shader.cpp', + 'dxvk_spec_const.cpp', 'dxvk_staging.cpp', 'dxvk_stats.cpp', 'dxvk_surface.cpp', 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 diff --git a/src/spirv/spirv_module.cpp b/src/spirv/spirv_module.cpp index caf90fef..c6262f5c 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, @@ -330,6 +340,19 @@ namespace dxvk { } + uint32_t SpirvModule::specConst32( + uint32_t typeId, + uint32_t value) { + uint32_t resultId = this->allocateId(); + + m_typeConstDefs.putIns (spv::OpSpecConstant, 4); + m_typeConstDefs.putWord (typeId); + m_typeConstDefs.putWord (resultId); + m_typeConstDefs.putWord (value); + return resultId; + } + + void SpirvModule::decorate( uint32_t object, spv::Decoration decoration) { diff --git a/src/spirv/spirv_module.h b/src/spirv/spirv_module.h index 93da6952..47f12f8e 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, @@ -154,6 +158,10 @@ namespace dxvk { uint32_t specConstBool( bool v); + uint32_t specConst32( + uint32_t typeId, + uint32_t value); + void decorate( uint32_t object, spv::Decoration decoration); 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;