mirror of
https://github.com/EduApps-CDG/OpenDX
synced 2024-12-30 09:45:37 +01:00
554 lines
23 KiB
C++
554 lines
23 KiB
C++
#include "../util/util_time.h"
|
|
|
|
#include "dxvk_device.h"
|
|
#include "dxvk_graphics.h"
|
|
#include "dxvk_pipemanager.h"
|
|
#include "dxvk_spec_const.h"
|
|
#include "dxvk_state_cache.h"
|
|
|
|
namespace dxvk {
|
|
|
|
DxvkGraphicsPipeline::DxvkGraphicsPipeline(
|
|
DxvkPipelineManager* pipeMgr,
|
|
DxvkGraphicsPipelineShaders shaders)
|
|
: m_vkd(pipeMgr->m_device->vkd()), m_pipeMgr(pipeMgr),
|
|
m_shaders(std::move(shaders)) {
|
|
if (m_shaders.vs != nullptr) m_shaders.vs ->defineResourceSlots(m_slotMapping);
|
|
if (m_shaders.tcs != nullptr) m_shaders.tcs->defineResourceSlots(m_slotMapping);
|
|
if (m_shaders.tes != nullptr) m_shaders.tes->defineResourceSlots(m_slotMapping);
|
|
if (m_shaders.gs != nullptr) m_shaders.gs ->defineResourceSlots(m_slotMapping);
|
|
if (m_shaders.fs != nullptr) m_shaders.fs ->defineResourceSlots(m_slotMapping);
|
|
|
|
m_slotMapping.makeDescriptorsDynamic(
|
|
pipeMgr->m_device->options().maxNumDynamicUniformBuffers,
|
|
pipeMgr->m_device->options().maxNumDynamicStorageBuffers);
|
|
|
|
m_layout = new DxvkPipelineLayout(m_vkd,
|
|
m_slotMapping, VK_PIPELINE_BIND_POINT_GRAPHICS);
|
|
|
|
m_vsIn = m_shaders.vs != nullptr ? m_shaders.vs->interfaceSlots().inputSlots : 0;
|
|
m_fsOut = m_shaders.fs != nullptr ? m_shaders.fs->interfaceSlots().outputSlots : 0;
|
|
|
|
if (m_shaders.gs != nullptr && m_shaders.gs->flags().test(DxvkShaderFlag::HasTransformFeedback))
|
|
m_flags.set(DxvkGraphicsPipelineFlag::HasTransformFeedback);
|
|
|
|
if (m_layout->getStorageDescriptorStages())
|
|
m_flags.set(DxvkGraphicsPipelineFlag::HasStorageDescriptors);
|
|
|
|
m_common.msSampleShadingEnable = m_shaders.fs != nullptr && m_shaders.fs->flags().test(DxvkShaderFlag::HasSampleRateShading);
|
|
m_common.msSampleShadingFactor = 1.0f;
|
|
}
|
|
|
|
|
|
DxvkGraphicsPipeline::~DxvkGraphicsPipeline() {
|
|
for (const auto& instance : m_pipelines)
|
|
this->destroyPipeline(instance.pipeline());
|
|
}
|
|
|
|
|
|
Rc<DxvkShader> DxvkGraphicsPipeline::getShader(
|
|
VkShaderStageFlagBits stage) const {
|
|
switch (stage) {
|
|
case VK_SHADER_STAGE_VERTEX_BIT: return m_shaders.vs;
|
|
case VK_SHADER_STAGE_GEOMETRY_BIT: return m_shaders.gs;
|
|
case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: return m_shaders.tcs;
|
|
case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: return m_shaders.tes;
|
|
case VK_SHADER_STAGE_FRAGMENT_BIT: return m_shaders.fs;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
VkPipeline DxvkGraphicsPipeline::getPipelineHandle(
|
|
const DxvkGraphicsPipelineStateInfo& state,
|
|
const DxvkRenderPass* renderPass) {
|
|
DxvkGraphicsPipelineInstance* instance = nullptr;
|
|
|
|
{ std::lock_guard<sync::Spinlock> lock(m_mutex);
|
|
|
|
instance = this->findInstance(state, renderPass);
|
|
|
|
if (instance)
|
|
return instance->pipeline();
|
|
|
|
instance = this->createInstance(state, renderPass);
|
|
}
|
|
|
|
if (!instance)
|
|
return VK_NULL_HANDLE;
|
|
|
|
this->writePipelineStateToCache(state, renderPass->format());
|
|
return instance->pipeline();
|
|
}
|
|
|
|
|
|
void DxvkGraphicsPipeline::compilePipeline(
|
|
const DxvkGraphicsPipelineStateInfo& state,
|
|
const DxvkRenderPass* renderPass) {
|
|
std::lock_guard<sync::Spinlock> lock(m_mutex);
|
|
|
|
if (!this->findInstance(state, renderPass))
|
|
this->createInstance(state, renderPass);
|
|
}
|
|
|
|
|
|
DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::createInstance(
|
|
const DxvkGraphicsPipelineStateInfo& state,
|
|
const DxvkRenderPass* renderPass) {
|
|
// If the pipeline state vector is invalid, don't try
|
|
// to create a new pipeline, it won't work anyway.
|
|
if (!this->validatePipelineState(state))
|
|
return nullptr;
|
|
|
|
VkPipeline newPipelineHandle = this->createPipeline(state, renderPass);
|
|
|
|
m_pipeMgr->m_numGraphicsPipelines += 1;
|
|
return &m_pipelines.emplace_back(state, renderPass, newPipelineHandle);
|
|
}
|
|
|
|
|
|
DxvkGraphicsPipelineInstance* DxvkGraphicsPipeline::findInstance(
|
|
const DxvkGraphicsPipelineStateInfo& state,
|
|
const DxvkRenderPass* renderPass) {
|
|
for (auto& instance : m_pipelines) {
|
|
if (instance.isCompatible(state, renderPass))
|
|
return &instance;
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
VkPipeline DxvkGraphicsPipeline::createPipeline(
|
|
const DxvkGraphicsPipelineStateInfo& state,
|
|
const DxvkRenderPass* renderPass) const {
|
|
if (Logger::logLevel() <= LogLevel::Debug) {
|
|
Logger::debug("Compiling graphics pipeline...");
|
|
this->logPipelineState(LogLevel::Debug, state);
|
|
}
|
|
|
|
// Render pass format and image layouts
|
|
DxvkRenderPassFormat passFormat = renderPass->format();
|
|
|
|
// Set up dynamic states as needed
|
|
std::array<VkDynamicState, 6> dynamicStates;
|
|
uint32_t dynamicStateCount = 0;
|
|
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
|
|
|
|
if (state.useDynamicDepthBias())
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_BIAS;
|
|
|
|
if (state.useDynamicDepthBounds())
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_BOUNDS;
|
|
|
|
if (state.useDynamicBlendConstants())
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;
|
|
|
|
if (state.useDynamicStencilRef())
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;
|
|
|
|
// Figure out the actual sample count to use
|
|
VkSampleCountFlagBits sampleCount = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
if (state.ms.sampleCount())
|
|
sampleCount = VkSampleCountFlagBits(state.ms.sampleCount());
|
|
else if (state.rs.sampleCount())
|
|
sampleCount = VkSampleCountFlagBits(state.rs.sampleCount());
|
|
|
|
// Set up some specialization constants
|
|
DxvkSpecConstants specData;
|
|
specData.set(uint32_t(DxvkSpecConstantId::RasterizerSampleCount), sampleCount, VK_SAMPLE_COUNT_1_BIT);
|
|
|
|
for (uint32_t i = 0; i < m_layout->bindingCount(); i++)
|
|
specData.set(i, state.bsBindingMask.test(i), true);
|
|
|
|
for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
|
|
if ((m_fsOut & (1 << i)) != 0) {
|
|
uint32_t specId = uint32_t(DxvkSpecConstantId::ColorComponentMappings) + 4 * i;
|
|
specData.set(specId + 0, state.omSwizzle[i].rIndex(), 0u);
|
|
specData.set(specId + 1, state.omSwizzle[i].gIndex(), 1u);
|
|
specData.set(specId + 2, state.omSwizzle[i].bIndex(), 2u);
|
|
specData.set(specId + 3, state.omSwizzle[i].aIndex(), 3u);
|
|
}
|
|
}
|
|
|
|
for (uint32_t i = 0; i < MaxNumSpecConstants; i++)
|
|
specData.set(getSpecId(i), state.sc.specConstants[i], 0u);
|
|
|
|
VkSpecializationInfo specInfo = specData.getSpecInfo();
|
|
|
|
auto vsm = createShaderModule(m_shaders.vs, state);
|
|
auto tcsm = createShaderModule(m_shaders.tcs, state);
|
|
auto tesm = createShaderModule(m_shaders.tes, state);
|
|
auto gsm = createShaderModule(m_shaders.gs, state);
|
|
auto fsm = createShaderModule(m_shaders.fs, state);
|
|
|
|
std::vector<VkPipelineShaderStageCreateInfo> stages;
|
|
if (vsm) stages.push_back(vsm.stageInfo(&specInfo));
|
|
if (tcsm) stages.push_back(tcsm.stageInfo(&specInfo));
|
|
if (tesm) stages.push_back(tesm.stageInfo(&specInfo));
|
|
if (gsm) stages.push_back(gsm.stageInfo(&specInfo));
|
|
if (fsm) stages.push_back(fsm.stageInfo(&specInfo));
|
|
|
|
// Fix up color write masks using the component mappings
|
|
std::array<VkPipelineColorBlendAttachmentState, MaxNumRenderTargets> omBlendAttachments;
|
|
|
|
const VkColorComponentFlags fullMask
|
|
= VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT
|
|
| VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
|
|
|
for (uint32_t i = 0; i < MaxNumRenderTargets; i++) {
|
|
omBlendAttachments[i] = state.omBlend[i].state();
|
|
|
|
if (omBlendAttachments[i].colorWriteMask != fullMask) {
|
|
omBlendAttachments[i].colorWriteMask = util::remapComponentMask(
|
|
state.omBlend[i].colorWriteMask(), state.omSwizzle[i].mapping());
|
|
}
|
|
|
|
if ((m_fsOut & (1 << i)) == 0)
|
|
omBlendAttachments[i].colorWriteMask = 0;
|
|
}
|
|
|
|
// Generate per-instance attribute divisors
|
|
std::array<VkVertexInputBindingDivisorDescriptionEXT, MaxNumVertexBindings> viDivisorDesc;
|
|
uint32_t viDivisorCount = 0;
|
|
|
|
for (uint32_t i = 0; i < state.il.bindingCount(); i++) {
|
|
if (state.ilBindings[i].inputRate() == VK_VERTEX_INPUT_RATE_INSTANCE
|
|
&& state.ilBindings[i].divisor() != 1) {
|
|
const uint32_t id = viDivisorCount++;
|
|
|
|
viDivisorDesc[id].binding = i; /* see below */
|
|
viDivisorDesc[id].divisor = state.ilBindings[i].divisor();
|
|
}
|
|
}
|
|
|
|
int32_t rasterizedStream = m_shaders.gs != nullptr
|
|
? m_shaders.gs->shaderOptions().rasterizedStream
|
|
: 0;
|
|
|
|
// Compact vertex bindings so that we can more easily update vertex buffers
|
|
std::array<VkVertexInputAttributeDescription, MaxNumVertexAttributes> viAttribs;
|
|
std::array<VkVertexInputBindingDescription, MaxNumVertexBindings> viBindings;
|
|
std::array<uint32_t, MaxNumVertexBindings> viBindingMap = { };
|
|
|
|
for (uint32_t i = 0; i < state.il.bindingCount(); i++) {
|
|
viBindings[i] = state.ilBindings[i].description();
|
|
viBindings[i].binding = i;
|
|
viBindingMap[state.ilBindings[i].binding()] = i;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < state.il.attributeCount(); i++) {
|
|
viAttribs[i] = state.ilAttributes[i].description();
|
|
viAttribs[i].binding = viBindingMap[state.ilAttributes[i].binding()];
|
|
}
|
|
|
|
VkPipelineVertexInputDivisorStateCreateInfoEXT viDivisorInfo;
|
|
viDivisorInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT;
|
|
viDivisorInfo.pNext = nullptr;
|
|
viDivisorInfo.vertexBindingDivisorCount = viDivisorCount;
|
|
viDivisorInfo.pVertexBindingDivisors = viDivisorDesc.data();
|
|
|
|
VkPipelineVertexInputStateCreateInfo viInfo;
|
|
viInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
|
viInfo.pNext = &viDivisorInfo;
|
|
viInfo.flags = 0;
|
|
viInfo.vertexBindingDescriptionCount = state.il.bindingCount();
|
|
viInfo.pVertexBindingDescriptions = viBindings.data();
|
|
viInfo.vertexAttributeDescriptionCount = state.il.attributeCount();
|
|
viInfo.pVertexAttributeDescriptions = viAttribs.data();
|
|
|
|
if (viDivisorCount == 0)
|
|
viInfo.pNext = viDivisorInfo.pNext;
|
|
|
|
// TODO remove this once the extension is widely supported
|
|
if (!m_pipeMgr->m_device->features().extVertexAttributeDivisor.vertexAttributeInstanceRateDivisor)
|
|
viInfo.pNext = viDivisorInfo.pNext;
|
|
|
|
VkPipelineInputAssemblyStateCreateInfo iaInfo;
|
|
iaInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
|
iaInfo.pNext = nullptr;
|
|
iaInfo.flags = 0;
|
|
iaInfo.topology = state.ia.primitiveTopology();
|
|
iaInfo.primitiveRestartEnable = state.ia.primitiveRestart();
|
|
|
|
VkPipelineTessellationStateCreateInfo tsInfo;
|
|
tsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
|
|
tsInfo.pNext = nullptr;
|
|
tsInfo.flags = 0;
|
|
tsInfo.patchControlPoints = state.ia.patchVertexCount();
|
|
|
|
VkPipelineViewportStateCreateInfo vpInfo;
|
|
vpInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
|
vpInfo.pNext = nullptr;
|
|
vpInfo.flags = 0;
|
|
vpInfo.viewportCount = state.rs.viewportCount();
|
|
vpInfo.pViewports = nullptr;
|
|
vpInfo.scissorCount = state.rs.viewportCount();
|
|
vpInfo.pScissors = nullptr;
|
|
|
|
VkPipelineRasterizationStateStreamCreateInfoEXT xfbStreamInfo;
|
|
xfbStreamInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT;
|
|
xfbStreamInfo.pNext = nullptr;
|
|
xfbStreamInfo.flags = 0;
|
|
xfbStreamInfo.rasterizationStream = uint32_t(rasterizedStream);
|
|
|
|
VkPipelineRasterizationDepthClipStateCreateInfoEXT rsDepthClipInfo;
|
|
rsDepthClipInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT;
|
|
rsDepthClipInfo.pNext = nullptr;
|
|
rsDepthClipInfo.flags = 0;
|
|
rsDepthClipInfo.depthClipEnable = state.rs.depthClipEnable();
|
|
|
|
VkPipelineRasterizationStateCreateInfo rsInfo;
|
|
rsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
|
rsInfo.pNext = &rsDepthClipInfo;
|
|
rsInfo.flags = 0;
|
|
rsInfo.depthClampEnable = VK_TRUE;
|
|
rsInfo.rasterizerDiscardEnable = rasterizedStream < 0;
|
|
rsInfo.polygonMode = state.rs.polygonMode();
|
|
rsInfo.cullMode = state.rs.cullMode();
|
|
rsInfo.frontFace = state.rs.frontFace();
|
|
rsInfo.depthBiasEnable = state.rs.depthBiasEnable();
|
|
rsInfo.depthBiasConstantFactor= 0.0f;
|
|
rsInfo.depthBiasClamp = 0.0f;
|
|
rsInfo.depthBiasSlopeFactor = 0.0f;
|
|
rsInfo.lineWidth = 1.0f;
|
|
|
|
if (rasterizedStream > 0)
|
|
rsDepthClipInfo.pNext = &xfbStreamInfo;
|
|
|
|
if (!m_pipeMgr->m_device->features().extDepthClipEnable.depthClipEnable) {
|
|
rsInfo.pNext = rsDepthClipInfo.pNext;
|
|
rsInfo.depthClampEnable = !state.rs.depthClipEnable();
|
|
}
|
|
|
|
uint32_t sampleMask = state.ms.sampleMask();
|
|
|
|
VkPipelineMultisampleStateCreateInfo msInfo;
|
|
msInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
|
msInfo.pNext = nullptr;
|
|
msInfo.flags = 0;
|
|
msInfo.rasterizationSamples = sampleCount;
|
|
msInfo.sampleShadingEnable = m_common.msSampleShadingEnable;
|
|
msInfo.minSampleShading = m_common.msSampleShadingFactor;
|
|
msInfo.pSampleMask = &sampleMask;
|
|
msInfo.alphaToCoverageEnable = state.ms.enableAlphaToCoverage();
|
|
msInfo.alphaToOneEnable = VK_FALSE;
|
|
|
|
VkPipelineDepthStencilStateCreateInfo dsInfo;
|
|
dsInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
|
dsInfo.pNext = nullptr;
|
|
dsInfo.flags = 0;
|
|
dsInfo.depthTestEnable = state.ds.enableDepthTest();
|
|
dsInfo.depthWriteEnable = state.ds.enableDepthWrite() && !util::isDepthReadOnlyLayout(passFormat.depth.layout);
|
|
dsInfo.depthCompareOp = state.ds.depthCompareOp();
|
|
dsInfo.depthBoundsTestEnable = state.ds.enableDepthBoundsTest();
|
|
dsInfo.stencilTestEnable = state.ds.enableStencilTest();
|
|
dsInfo.front = state.dsFront.state();
|
|
dsInfo.back = state.dsBack.state();
|
|
dsInfo.minDepthBounds = 0.0f;
|
|
dsInfo.maxDepthBounds = 1.0f;
|
|
|
|
VkPipelineColorBlendStateCreateInfo cbInfo;
|
|
cbInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
|
cbInfo.pNext = nullptr;
|
|
cbInfo.flags = 0;
|
|
cbInfo.logicOpEnable = state.om.enableLogicOp();
|
|
cbInfo.logicOp = state.om.logicOp();
|
|
cbInfo.attachmentCount = DxvkLimits::MaxNumRenderTargets;
|
|
cbInfo.pAttachments = omBlendAttachments.data();
|
|
|
|
for (uint32_t i = 0; i < 4; i++)
|
|
cbInfo.blendConstants[i] = 0.0f;
|
|
|
|
VkPipelineDynamicStateCreateInfo dyInfo;
|
|
dyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
|
dyInfo.pNext = nullptr;
|
|
dyInfo.flags = 0;
|
|
dyInfo.dynamicStateCount = dynamicStateCount;
|
|
dyInfo.pDynamicStates = dynamicStates.data();
|
|
|
|
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 = &viInfo;
|
|
info.pInputAssemblyState = &iaInfo;
|
|
info.pTessellationState = &tsInfo;
|
|
info.pViewportState = &vpInfo;
|
|
info.pRasterizationState = &rsInfo;
|
|
info.pMultisampleState = &msInfo;
|
|
info.pDepthStencilState = &dsInfo;
|
|
info.pColorBlendState = &cbInfo;
|
|
info.pDynamicState = &dyInfo;
|
|
info.layout = m_layout->pipelineLayout();
|
|
info.renderPass = renderPass->getDefaultHandle();
|
|
info.subpass = 0;
|
|
info.basePipelineHandle = VK_NULL_HANDLE;
|
|
info.basePipelineIndex = -1;
|
|
|
|
if (tsInfo.patchControlPoints == 0)
|
|
info.pTessellationState = nullptr;
|
|
|
|
// Time pipeline compilation for debugging purposes
|
|
dxvk::high_resolution_clock::time_point t0, t1;
|
|
|
|
if (Logger::logLevel() <= LogLevel::Debug)
|
|
t0 = dxvk::high_resolution_clock::now();
|
|
|
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
|
if (m_vkd->vkCreateGraphicsPipelines(m_vkd->device(),
|
|
m_pipeMgr->m_cache->handle(), 1, &info, nullptr, &pipeline) != VK_SUCCESS) {
|
|
Logger::err("DxvkGraphicsPipeline: Failed to compile pipeline");
|
|
this->logPipelineState(LogLevel::Error, state);
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
if (Logger::logLevel() <= LogLevel::Debug) {
|
|
t1 = dxvk::high_resolution_clock::now();
|
|
auto td = std::chrono::duration_cast<std::chrono::milliseconds>(t1 - t0);
|
|
Logger::debug(str::format("DxvkGraphicsPipeline: Finished in ", td.count(), " ms"));
|
|
}
|
|
|
|
return pipeline;
|
|
}
|
|
|
|
|
|
void DxvkGraphicsPipeline::destroyPipeline(VkPipeline pipeline) const {
|
|
m_vkd->vkDestroyPipeline(m_vkd->device(), pipeline, nullptr);
|
|
}
|
|
|
|
|
|
DxvkShaderModule DxvkGraphicsPipeline::createShaderModule(
|
|
const Rc<DxvkShader>& shader,
|
|
const DxvkGraphicsPipelineStateInfo& state) const {
|
|
if (shader == nullptr)
|
|
return DxvkShaderModule();
|
|
|
|
DxvkShaderModuleCreateInfo info;
|
|
|
|
// Fix up fragment shader outputs for dual-source blending
|
|
if (shader->stage() == VK_SHADER_STAGE_FRAGMENT_BIT) {
|
|
info.fsDualSrcBlend = state.omBlend[0].blendEnable() && (
|
|
util::isDualSourceBlendFactor(state.omBlend[0].srcColorBlendFactor()) ||
|
|
util::isDualSourceBlendFactor(state.omBlend[0].dstColorBlendFactor()) ||
|
|
util::isDualSourceBlendFactor(state.omBlend[0].srcAlphaBlendFactor()) ||
|
|
util::isDualSourceBlendFactor(state.omBlend[0].dstAlphaBlendFactor()));
|
|
}
|
|
|
|
// Deal with undefined shader inputs
|
|
uint32_t consumedInputs = shader->interfaceSlots().inputSlots;
|
|
uint32_t providedInputs = 0;
|
|
|
|
if (shader->stage() == VK_SHADER_STAGE_VERTEX_BIT) {
|
|
for (uint32_t i = 0; i < state.il.attributeCount(); i++)
|
|
providedInputs |= 1u << state.ilAttributes[i].location();
|
|
} else if (shader->stage() != VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) {
|
|
auto prevStage = getPrevStageShader(shader->stage());
|
|
providedInputs = prevStage->interfaceSlots().outputSlots;
|
|
} else {
|
|
// Technically not correct, but this
|
|
// would need a lot of extra care
|
|
providedInputs = consumedInputs;
|
|
}
|
|
|
|
info.undefinedInputs = (providedInputs & consumedInputs) ^ consumedInputs;
|
|
return shader->createShaderModule(m_vkd, m_slotMapping, info);
|
|
}
|
|
|
|
|
|
Rc<DxvkShader> DxvkGraphicsPipeline::getPrevStageShader(VkShaderStageFlagBits stage) const {
|
|
if (stage == VK_SHADER_STAGE_VERTEX_BIT)
|
|
return nullptr;
|
|
|
|
if (stage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT)
|
|
return m_shaders.tcs;
|
|
|
|
Rc<DxvkShader> result = m_shaders.vs;
|
|
|
|
if (stage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT)
|
|
return result;
|
|
|
|
if (m_shaders.tes != nullptr)
|
|
result = m_shaders.tes;
|
|
|
|
if (stage == VK_SHADER_STAGE_GEOMETRY_BIT)
|
|
return result;
|
|
|
|
if (m_shaders.gs != nullptr)
|
|
result = m_shaders.gs;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
bool DxvkGraphicsPipeline::validatePipelineState(
|
|
const DxvkGraphicsPipelineStateInfo& state) const {
|
|
// Tessellation shaders and patches must be used together
|
|
bool hasPatches = state.ia.primitiveTopology() == VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
|
|
|
|
bool hasTcs = m_shaders.tcs != nullptr;
|
|
bool hasTes = m_shaders.tes != nullptr;
|
|
|
|
if (hasPatches != hasTcs || hasPatches != hasTes)
|
|
return false;
|
|
|
|
// Filter out undefined primitive topologies
|
|
if (state.ia.primitiveTopology() == VK_PRIMITIVE_TOPOLOGY_MAX_ENUM)
|
|
return false;
|
|
|
|
// Prevent unintended out-of-bounds access to the IL arrays
|
|
if (state.il.attributeCount() > DxvkLimits::MaxNumVertexAttributes
|
|
|| state.il.bindingCount() > DxvkLimits::MaxNumVertexBindings)
|
|
return false;
|
|
|
|
// No errors
|
|
return true;
|
|
}
|
|
|
|
|
|
void DxvkGraphicsPipeline::writePipelineStateToCache(
|
|
const DxvkGraphicsPipelineStateInfo& state,
|
|
const DxvkRenderPassFormat& format) const {
|
|
if (m_pipeMgr->m_stateCache == nullptr)
|
|
return;
|
|
|
|
DxvkStateCacheKey key;
|
|
if (m_shaders.vs != nullptr) key.vs = m_shaders.vs->getShaderKey();
|
|
if (m_shaders.tcs != nullptr) key.tcs = m_shaders.tcs->getShaderKey();
|
|
if (m_shaders.tes != nullptr) key.tes = m_shaders.tes->getShaderKey();
|
|
if (m_shaders.gs != nullptr) key.gs = m_shaders.gs->getShaderKey();
|
|
if (m_shaders.fs != nullptr) key.fs = m_shaders.fs->getShaderKey();
|
|
|
|
m_pipeMgr->m_stateCache->addGraphicsPipeline(key, state, format);
|
|
}
|
|
|
|
|
|
void DxvkGraphicsPipeline::logPipelineState(
|
|
LogLevel level,
|
|
const DxvkGraphicsPipelineStateInfo& state) const {
|
|
if (m_shaders.vs != nullptr) Logger::log(level, str::format(" vs : ", m_shaders.vs ->debugName()));
|
|
if (m_shaders.tcs != nullptr) Logger::log(level, str::format(" tcs : ", m_shaders.tcs->debugName()));
|
|
if (m_shaders.tes != nullptr) Logger::log(level, str::format(" tes : ", m_shaders.tes->debugName()));
|
|
if (m_shaders.gs != nullptr) Logger::log(level, str::format(" gs : ", m_shaders.gs ->debugName()));
|
|
if (m_shaders.fs != nullptr) Logger::log(level, str::format(" fs : ", m_shaders.fs ->debugName()));
|
|
|
|
for (uint32_t i = 0; i < state.il.attributeCount(); i++) {
|
|
const auto& attr = state.ilAttributes[i];
|
|
Logger::log(level, str::format(" attr ", i, " : location ", attr.location(), ", binding ", attr.binding(), ", format ", attr.format(), ", offset ", attr.offset()));
|
|
}
|
|
for (uint32_t i = 0; i < state.il.bindingCount(); i++) {
|
|
const auto& bind = state.ilBindings[i];
|
|
Logger::log(level, str::format(" binding ", i, " : binding ", bind.binding(), ", stride ", bind.stride(), ", rate ", bind.inputRate(), ", divisor ", bind.divisor()));
|
|
}
|
|
|
|
// TODO log more pipeline state
|
|
}
|
|
|
|
}
|