mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-03-14 22:29:15 +01:00
671 lines
23 KiB
C++
671 lines
23 KiB
C++
#include "dxvk_device.h"
|
|
#include "dxvk_pipemanager.h"
|
|
#include "dxvk_shader.h"
|
|
|
|
#include <dxvk_dummy_frag.h>
|
|
|
|
#include <algorithm>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
|
|
namespace dxvk {
|
|
|
|
DxvkShader::DxvkShader(
|
|
const DxvkShaderCreateInfo& info,
|
|
SpirvCodeBuffer&& spirv)
|
|
: m_info(info), m_code(spirv), m_bindings(info.stage) {
|
|
m_info.uniformData = nullptr;
|
|
m_info.bindings = nullptr;
|
|
|
|
// Copy resource binding slot infos
|
|
for (uint32_t i = 0; i < info.bindingCount; i++) {
|
|
DxvkBindingInfo binding = info.bindings[i];
|
|
binding.stages = info.stage;
|
|
m_bindings.addBinding(binding);
|
|
}
|
|
|
|
if (info.pushConstSize) {
|
|
VkPushConstantRange pushConst;
|
|
pushConst.stageFlags = info.stage;
|
|
pushConst.offset = info.pushConstOffset;
|
|
pushConst.size = info.pushConstSize;
|
|
|
|
m_bindings.addPushConstantRange(pushConst);
|
|
}
|
|
|
|
// Copy uniform buffer data
|
|
if (info.uniformSize) {
|
|
m_uniformData.resize(info.uniformSize);
|
|
std::memcpy(m_uniformData.data(), info.uniformData, info.uniformSize);
|
|
m_info.uniformData = m_uniformData.data();
|
|
}
|
|
|
|
// Run an analysis pass over the SPIR-V code to gather some
|
|
// info that we may need during pipeline compilation.
|
|
std::vector<BindingOffsets> bindingOffsets;
|
|
std::vector<uint32_t> varIds;
|
|
|
|
SpirvCodeBuffer code = std::move(spirv);
|
|
uint32_t o1VarId = 0;
|
|
|
|
for (auto ins : code) {
|
|
if (ins.opCode() == spv::OpDecorate) {
|
|
if (ins.arg(2) == spv::DecorationBinding) {
|
|
uint32_t varId = ins.arg(1);
|
|
bindingOffsets.resize(std::max(bindingOffsets.size(), size_t(varId + 1)));
|
|
bindingOffsets[varId].bindingId = ins.arg(3);
|
|
bindingOffsets[varId].bindingOffset = ins.offset() + 3;
|
|
varIds.push_back(varId);
|
|
}
|
|
|
|
if (ins.arg(2) == spv::DecorationDescriptorSet) {
|
|
uint32_t varId = ins.arg(1);
|
|
bindingOffsets.resize(std::max(bindingOffsets.size(), size_t(varId + 1)));
|
|
bindingOffsets[varId].setOffset = ins.offset() + 3;
|
|
}
|
|
|
|
if (ins.arg(2) == spv::DecorationSpecId && ins.arg(3) < MaxNumSpecConstants)
|
|
m_flags.set(DxvkShaderFlag::HasSpecConstants);
|
|
|
|
if (ins.arg(2) == spv::DecorationLocation && ins.arg(3) == 1) {
|
|
m_o1LocOffset = ins.offset() + 3;
|
|
o1VarId = ins.arg(1);
|
|
}
|
|
|
|
if (ins.arg(2) == spv::DecorationIndex && ins.arg(1) == o1VarId)
|
|
m_o1IdxOffset = ins.offset() + 3;
|
|
}
|
|
|
|
if (ins.opCode() == spv::OpExecutionMode) {
|
|
if (ins.arg(2) == spv::ExecutionModeStencilRefReplacingEXT)
|
|
m_flags.set(DxvkShaderFlag::ExportsStencilRef);
|
|
|
|
if (ins.arg(2) == spv::ExecutionModeXfb)
|
|
m_flags.set(DxvkShaderFlag::HasTransformFeedback);
|
|
}
|
|
|
|
if (ins.opCode() == spv::OpCapability) {
|
|
if (ins.arg(1) == spv::CapabilitySampleRateShading)
|
|
m_flags.set(DxvkShaderFlag::HasSampleRateShading);
|
|
|
|
if (ins.arg(1) == spv::CapabilityShaderViewportIndexLayerEXT)
|
|
m_flags.set(DxvkShaderFlag::ExportsViewportIndexLayerFromVertexStage);
|
|
}
|
|
|
|
// Ignore the actual shader code, there's nothing interesting for us in there.
|
|
if (ins.opCode() == spv::OpFunction)
|
|
break;
|
|
}
|
|
|
|
// Combine spec constant IDs with other binding info
|
|
for (auto varId : varIds) {
|
|
BindingOffsets info = bindingOffsets[varId];
|
|
|
|
if (info.bindingOffset)
|
|
m_bindingOffsets.push_back(info);
|
|
}
|
|
}
|
|
|
|
|
|
DxvkShader::~DxvkShader() {
|
|
|
|
}
|
|
|
|
|
|
SpirvCodeBuffer DxvkShader::getCode(
|
|
const DxvkBindingLayoutObjects* layout,
|
|
const DxvkShaderModuleCreateInfo& state) const {
|
|
SpirvCodeBuffer spirvCode = m_code.decompress();
|
|
uint32_t* code = spirvCode.data();
|
|
|
|
// Remap resource binding IDs
|
|
for (const auto& info : m_bindingOffsets) {
|
|
auto mappedBinding = layout->lookupBinding(info.bindingId);
|
|
|
|
if (mappedBinding) {
|
|
code[info.bindingOffset] = mappedBinding->binding;
|
|
|
|
if (info.setOffset)
|
|
code[info.setOffset] = mappedBinding->set;
|
|
}
|
|
}
|
|
|
|
// For dual-source blending we need to re-map
|
|
// location 1, index 0 to location 0, index 1
|
|
if (state.fsDualSrcBlend && m_o1IdxOffset && m_o1LocOffset)
|
|
std::swap(code[m_o1IdxOffset], code[m_o1LocOffset]);
|
|
|
|
// Replace undefined input variables with zero
|
|
for (uint32_t u : bit::BitMask(state.undefinedInputs))
|
|
eliminateInput(spirvCode, u);
|
|
|
|
return spirvCode;
|
|
}
|
|
|
|
|
|
bool DxvkShader::canUsePipelineLibrary() const {
|
|
// Pipeline libraries are unsupported for geometry and
|
|
// tessellation stages since we'd need to compile them
|
|
// all into one library
|
|
if (m_info.stage != VK_SHADER_STAGE_VERTEX_BIT
|
|
&& m_info.stage != VK_SHADER_STAGE_FRAGMENT_BIT
|
|
&& m_info.stage != VK_SHADER_STAGE_COMPUTE_BIT)
|
|
return false;
|
|
|
|
// Ignore shaders that have user-defined spec constants
|
|
return !m_flags.test(DxvkShaderFlag::HasSpecConstants);
|
|
}
|
|
|
|
|
|
void DxvkShader::dump(std::ostream& outputStream) const {
|
|
m_code.decompress().store(outputStream);
|
|
}
|
|
|
|
|
|
void DxvkShader::eliminateInput(SpirvCodeBuffer& code, uint32_t location) {
|
|
struct SpirvTypeInfo {
|
|
spv::Op op = spv::OpNop;
|
|
uint32_t baseTypeId = 0;
|
|
uint32_t compositeSize = 0;
|
|
spv::StorageClass storageClass = spv::StorageClassMax;
|
|
};
|
|
|
|
std::unordered_map<uint32_t, SpirvTypeInfo> types;
|
|
std::unordered_map<uint32_t, uint32_t> constants;
|
|
std::unordered_set<uint32_t> candidates;
|
|
|
|
// Find the input variable in question
|
|
size_t inputVarOffset = 0;
|
|
uint32_t inputVarTypeId = 0;
|
|
uint32_t inputVarId = 0;
|
|
|
|
for (auto ins : code) {
|
|
if (ins.opCode() == spv::OpDecorate) {
|
|
if (ins.arg(2) == spv::DecorationLocation
|
|
&& ins.arg(3) == location)
|
|
candidates.insert(ins.arg(1));
|
|
}
|
|
|
|
if (ins.opCode() == spv::OpConstant)
|
|
constants.insert({ ins.arg(2), ins.arg(3) });
|
|
|
|
if (ins.opCode() == spv::OpTypeFloat || ins.opCode() == spv::OpTypeInt)
|
|
types.insert({ ins.arg(1), { ins.opCode(), 0, ins.arg(2), spv::StorageClassMax }});
|
|
|
|
if (ins.opCode() == spv::OpTypeVector)
|
|
types.insert({ ins.arg(1), { ins.opCode(), ins.arg(2), ins.arg(3), spv::StorageClassMax }});
|
|
|
|
if (ins.opCode() == spv::OpTypeArray) {
|
|
auto constant = constants.find(ins.arg(3));
|
|
if (constant == constants.end())
|
|
continue;
|
|
types.insert({ ins.arg(1), { ins.opCode(), ins.arg(2), constant->second, spv::StorageClassMax }});
|
|
}
|
|
|
|
if (ins.opCode() == spv::OpTypePointer)
|
|
types.insert({ ins.arg(1), { ins.opCode(), ins.arg(3), 0, spv::StorageClass(ins.arg(2)) }});
|
|
|
|
if (ins.opCode() == spv::OpVariable && spv::StorageClass(ins.arg(3)) == spv::StorageClassInput) {
|
|
if (candidates.find(ins.arg(2)) != candidates.end()) {
|
|
inputVarOffset = ins.offset();
|
|
inputVarTypeId = ins.arg(1);
|
|
inputVarId = ins.arg(2);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!inputVarId)
|
|
return;
|
|
|
|
// Declare private pointer types
|
|
auto pointerType = types.find(inputVarTypeId);
|
|
if (pointerType == types.end())
|
|
return;
|
|
|
|
code.beginInsertion(inputVarOffset);
|
|
std::vector<std::pair<uint32_t, SpirvTypeInfo>> privateTypes;
|
|
|
|
for (auto p = types.find(pointerType->second.baseTypeId);
|
|
p != types.end();
|
|
p = types.find(p->second.baseTypeId)) {
|
|
std::pair<uint32_t, SpirvTypeInfo> info = *p;
|
|
info.first = 0;
|
|
info.second.baseTypeId = p->first;
|
|
info.second.storageClass = spv::StorageClassPrivate;
|
|
|
|
for (auto t : types) {
|
|
if (t.second.op == info.second.op
|
|
&& t.second.baseTypeId == info.second.baseTypeId
|
|
&& t.second.storageClass == info.second.storageClass)
|
|
info.first = t.first;
|
|
}
|
|
|
|
if (!info.first) {
|
|
info.first = code.allocId();
|
|
|
|
code.putIns(spv::OpTypePointer, 4);
|
|
code.putWord(info.first);
|
|
code.putWord(info.second.storageClass);
|
|
code.putWord(info.second.baseTypeId);
|
|
}
|
|
|
|
privateTypes.push_back(info);
|
|
}
|
|
|
|
// Define zero constants
|
|
uint32_t constantId = 0;
|
|
|
|
for (auto i = privateTypes.rbegin(); i != privateTypes.rend(); i++) {
|
|
if (constantId) {
|
|
uint32_t compositeSize = i->second.compositeSize;
|
|
uint32_t compositeId = code.allocId();
|
|
|
|
code.putIns(spv::OpConstantComposite, 3 + compositeSize);
|
|
code.putWord(i->second.baseTypeId);
|
|
code.putWord(compositeId);
|
|
|
|
for (uint32_t i = 0; i < compositeSize; i++)
|
|
code.putWord(constantId);
|
|
|
|
constantId = compositeId;
|
|
} else {
|
|
constantId = code.allocId();
|
|
|
|
code.putIns(spv::OpConstant, 4);
|
|
code.putWord(i->second.baseTypeId);
|
|
code.putWord(constantId);
|
|
code.putWord(0);
|
|
}
|
|
}
|
|
|
|
// Erase and re-declare variable
|
|
code.erase(4);
|
|
|
|
code.putIns(spv::OpVariable, 5);
|
|
code.putWord(privateTypes[0].first);
|
|
code.putWord(inputVarId);
|
|
code.putWord(spv::StorageClassPrivate);
|
|
code.putWord(constantId);
|
|
|
|
code.endInsertion();
|
|
|
|
// Remove variable from interface list
|
|
for (auto ins : code) {
|
|
if (ins.opCode() == spv::OpEntryPoint) {
|
|
uint32_t argIdx = 2 + code.strLen(ins.chr(2));
|
|
|
|
while (argIdx < ins.length()) {
|
|
if (ins.arg(argIdx) == inputVarId) {
|
|
ins.setArg(0, spv::OpEntryPoint | ((ins.length() - 1) << spv::WordCountShift));
|
|
|
|
code.beginInsertion(ins.offset() + argIdx);
|
|
code.erase(1);
|
|
code.endInsertion();
|
|
break;
|
|
}
|
|
|
|
argIdx += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove location and other declarations
|
|
for (auto iter = code.begin(); iter != code.end(); ) {
|
|
auto ins = *(iter++);
|
|
|
|
if (ins.opCode() == spv::OpDecorate && ins.arg(1) == inputVarId) {
|
|
uint32_t numWords;
|
|
|
|
switch (ins.arg(2)) {
|
|
case spv::DecorationLocation:
|
|
case spv::DecorationFlat:
|
|
case spv::DecorationNoPerspective:
|
|
case spv::DecorationCentroid:
|
|
case spv::DecorationPatch:
|
|
case spv::DecorationSample:
|
|
numWords = ins.length();
|
|
break;
|
|
|
|
default:
|
|
numWords = 0;
|
|
}
|
|
|
|
if (numWords) {
|
|
code.beginInsertion(ins.offset());
|
|
code.erase(numWords);
|
|
|
|
iter = SpirvInstructionIterator(code.data(), code.endInsertion(), code.dwords());
|
|
}
|
|
}
|
|
|
|
if (ins.opCode() == spv::OpFunction)
|
|
break;
|
|
}
|
|
|
|
// Fix up pointer types used in access chain instructions
|
|
std::unordered_map<uint32_t, uint32_t> accessChainIds;
|
|
|
|
for (auto ins : code) {
|
|
if (ins.opCode() == spv::OpAccessChain
|
|
|| ins.opCode() == spv::OpInBoundsAccessChain) {
|
|
uint32_t depth = ins.length() - 4;
|
|
|
|
if (ins.arg(3) == inputVarId) {
|
|
// Access chains accessing the variable directly
|
|
ins.setArg(1, privateTypes.at(depth).first);
|
|
accessChainIds.insert({ ins.arg(2), depth });
|
|
} else {
|
|
// Access chains derived from the variable
|
|
auto entry = accessChainIds.find(ins.arg(2));
|
|
if (entry != accessChainIds.end()) {
|
|
depth += entry->second;
|
|
ins.setArg(1, privateTypes.at(depth).first);
|
|
accessChainIds.insert({ ins.arg(2), depth });
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
DxvkShaderStageInfo::DxvkShaderStageInfo(const DxvkDevice* device)
|
|
: m_device(device) {
|
|
|
|
}
|
|
|
|
void DxvkShaderStageInfo::addStage(
|
|
VkShaderStageFlagBits stage,
|
|
SpirvCodeBuffer&& code,
|
|
const VkSpecializationInfo* specInfo) {
|
|
// Take ownership of the SPIR-V code buffer
|
|
auto& codeBuffer = m_codeBuffers[m_stageCount];
|
|
codeBuffer = std::move(code);
|
|
|
|
// For graphics pipelines, as long as graphics pipeline libraries are
|
|
// enabled, we do not need to create a shader module object and can
|
|
// instead chain the create info to the shader stage info struct.
|
|
// For compute pipelines, this doesn't work and we still need a module.
|
|
auto& moduleInfo = m_moduleInfos[m_stageCount];
|
|
moduleInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
|
moduleInfo.codeSize = codeBuffer.size();
|
|
moduleInfo.pCode = codeBuffer.data();
|
|
|
|
VkShaderModule shaderModule = VK_NULL_HANDLE;
|
|
if (!m_device->features().extGraphicsPipelineLibrary.graphicsPipelineLibrary || stage == VK_SHADER_STAGE_COMPUTE_BIT) {
|
|
auto vk = m_device->vkd();
|
|
|
|
if (vk->vkCreateShaderModule(vk->device(), &moduleInfo, nullptr, &shaderModule))
|
|
throw DxvkError("DxvkShaderStageInfo: Failed to create shader module");
|
|
}
|
|
|
|
// Set up shader stage info with the data provided
|
|
auto& stageInfo = m_stageInfos[m_stageCount];
|
|
stageInfo = { VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO };
|
|
|
|
if (!stageInfo.module)
|
|
stageInfo.pNext = &moduleInfo;
|
|
|
|
stageInfo.stage = stage;
|
|
stageInfo.module = shaderModule;
|
|
stageInfo.pName = "main";
|
|
stageInfo.pSpecializationInfo = specInfo;
|
|
|
|
m_stageCount++;
|
|
}
|
|
|
|
|
|
DxvkShaderStageInfo::~DxvkShaderStageInfo() {
|
|
auto vk = m_device->vkd();
|
|
|
|
for (uint32_t i = 0; i < m_stageCount; i++) {
|
|
if (m_stageInfos[i].module)
|
|
vk->vkDestroyShaderModule(vk->device(), m_stageInfos[i].module, nullptr);
|
|
}
|
|
}
|
|
|
|
|
|
DxvkShaderPipelineLibrary::DxvkShaderPipelineLibrary(
|
|
const DxvkDevice* device,
|
|
DxvkPipelineManager* manager,
|
|
const DxvkShader* shader,
|
|
const DxvkBindingLayoutObjects* layout)
|
|
: m_device (device),
|
|
m_cache (&manager->m_cache),
|
|
m_stats (&manager->m_stats),
|
|
m_shader (shader),
|
|
m_layout (layout) {
|
|
|
|
}
|
|
|
|
|
|
DxvkShaderPipelineLibrary::~DxvkShaderPipelineLibrary() {
|
|
auto vk = m_device->vkd();
|
|
|
|
vk->vkDestroyPipeline(vk->device(), m_pipeline, nullptr);
|
|
vk->vkDestroyPipeline(vk->device(), m_pipelineNoDepthClip, nullptr);
|
|
}
|
|
|
|
|
|
VkPipeline DxvkShaderPipelineLibrary::getPipelineHandle(
|
|
const DxvkShaderPipelineLibraryCompileArgs& args) {
|
|
std::lock_guard lock(m_mutex);
|
|
|
|
VkShaderStageFlagBits stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
|
|
if (m_shader)
|
|
stage = m_shader->info().stage;
|
|
|
|
VkPipeline& pipeline = (stage == VK_SHADER_STAGE_VERTEX_BIT && !args.depthClipEnable)
|
|
? m_pipelineNoDepthClip
|
|
: m_pipeline;
|
|
|
|
if (pipeline)
|
|
return pipeline;
|
|
|
|
switch (stage) {
|
|
case VK_SHADER_STAGE_VERTEX_BIT:
|
|
pipeline = compileVertexShaderPipeline(args);
|
|
break;
|
|
|
|
case VK_SHADER_STAGE_FRAGMENT_BIT:
|
|
pipeline = compileFragmentShaderPipeline();
|
|
break;
|
|
|
|
case VK_SHADER_STAGE_COMPUTE_BIT:
|
|
pipeline = compileComputeShaderPipeline();
|
|
break;
|
|
|
|
default:
|
|
// Should be unreachable
|
|
return VK_NULL_HANDLE;
|
|
}
|
|
|
|
if (args == DxvkShaderPipelineLibraryCompileArgs())
|
|
m_stats->numGraphicsLibraries += 1;
|
|
|
|
return pipeline;
|
|
}
|
|
|
|
|
|
void DxvkShaderPipelineLibrary::compilePipeline() {
|
|
// Just compile the pipeline with default args. Implicitly skips
|
|
// this step if another thread has compiled the pipeline in the
|
|
// meantime, in order to avoid duplicate work.
|
|
getPipelineHandle(DxvkShaderPipelineLibraryCompileArgs());
|
|
}
|
|
|
|
|
|
VkPipeline DxvkShaderPipelineLibrary::compileVertexShaderPipeline(
|
|
const DxvkShaderPipelineLibraryCompileArgs& args) {
|
|
auto vk = m_device->vkd();
|
|
|
|
DxvkShaderStageInfo stageInfo(m_device);
|
|
stageInfo.addStage(VK_SHADER_STAGE_VERTEX_BIT,
|
|
m_shader->getCode(m_layout, DxvkShaderModuleCreateInfo()), nullptr);
|
|
|
|
// Set up dynamic state. We do not know any pipeline state
|
|
// at this time, so make as much state dynamic as we can.
|
|
std::array<VkDynamicState, 5> dynamicStates = {{
|
|
VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT_EXT,
|
|
VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT_EXT,
|
|
VK_DYNAMIC_STATE_DEPTH_BIAS,
|
|
VK_DYNAMIC_STATE_CULL_MODE_EXT,
|
|
VK_DYNAMIC_STATE_FRONT_FACE_EXT,
|
|
}};
|
|
|
|
VkPipelineDynamicStateCreateInfo dyInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
|
dyInfo.dynamicStateCount = dynamicStates.size();
|
|
dyInfo.pDynamicStates = dynamicStates.data();
|
|
|
|
// All viewport state is dynamic, so we do not need to initialize this.
|
|
VkPipelineViewportStateCreateInfo vpInfo = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
|
|
|
|
// Set up rasterizer state. Depth bias, cull mode and front face are all
|
|
// dynamic, but we do not have dynamic state for depth bias enablement
|
|
// with the original version of VK_EXT_extended_dynamic_state, so always
|
|
// enable that. Do not support any polygon modes other than FILL.
|
|
VkPipelineRasterizationDepthClipStateCreateInfoEXT rsDepthClipInfo = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT };
|
|
|
|
VkPipelineRasterizationStateCreateInfo rsInfo = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
|
rsInfo.depthClampEnable = VK_TRUE;
|
|
rsInfo.rasterizerDiscardEnable = VK_FALSE;
|
|
rsInfo.polygonMode = VK_POLYGON_MODE_FILL;
|
|
rsInfo.depthBiasEnable = VK_TRUE;
|
|
rsInfo.lineWidth = 1.0f;
|
|
|
|
if (m_device->features().extDepthClipEnable.depthClipEnable) {
|
|
rsDepthClipInfo.pNext = std::exchange(rsInfo.pNext, &rsDepthClipInfo);
|
|
rsDepthClipInfo.depthClipEnable = args.depthClipEnable;
|
|
} else {
|
|
rsInfo.depthClampEnable = !args.depthClipEnable;
|
|
}
|
|
|
|
// Only the view mask is used as input, and since we do not use MultiView, it is always 0
|
|
VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR };
|
|
|
|
VkGraphicsPipelineLibraryCreateInfoEXT libInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, &rtInfo };
|
|
libInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_PRE_RASTERIZATION_SHADERS_BIT_EXT;
|
|
|
|
VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &libInfo };
|
|
info.flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
|
|
info.stageCount = stageInfo.getStageCount();
|
|
info.pStages = stageInfo.getStageInfos();
|
|
info.pViewportState = &vpInfo;
|
|
info.pRasterizationState = &rsInfo;
|
|
info.pDynamicState = &dyInfo;
|
|
info.layout = m_layout->getPipelineLayout(true);
|
|
info.basePipelineIndex = -1;
|
|
|
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
|
|
|
if (vk->vkCreateGraphicsPipelines(vk->device(), m_cache->handle(), 1, &info, nullptr, &pipeline))
|
|
throw DxvkError("DxvkShaderPipelineLibrary: Failed to create compute pipeline");
|
|
|
|
return pipeline;
|
|
}
|
|
|
|
|
|
VkPipeline DxvkShaderPipelineLibrary::compileFragmentShaderPipeline() {
|
|
auto vk = m_device->vkd();
|
|
|
|
// Initialize code buffer. As a special case, it is possible that
|
|
// we have to deal with a null shader, but the pipeline library
|
|
// extension requires us to always specify a fragment shader.
|
|
DxvkShaderStageInfo stageInfo(m_device);
|
|
|
|
if (m_shader) {
|
|
stageInfo.addStage(VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
m_shader->getCode(m_layout, DxvkShaderModuleCreateInfo()), nullptr);
|
|
} else {
|
|
stageInfo.addStage(VK_SHADER_STAGE_FRAGMENT_BIT,
|
|
SpirvCodeBuffer(dxvk_dummy_frag), nullptr);
|
|
}
|
|
|
|
// Set up dynamic state. We do not know any pipeline state
|
|
// at this time, so make as much state dynamic as we can.
|
|
uint32_t dynamicStateCount = 0;
|
|
std::array<VkDynamicState, 10> dynamicStates;
|
|
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT;
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT;
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_COMPARE_OP_EXT;
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK;
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_WRITE_MASK;
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_REFERENCE;
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE_EXT;
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_STENCIL_OP_EXT;
|
|
|
|
if (m_device->features().core.features.depthBounds) {
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_BOUNDS_TEST_ENABLE_EXT;
|
|
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_BOUNDS;
|
|
}
|
|
|
|
VkPipelineDynamicStateCreateInfo dyInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
|
dyInfo.dynamicStateCount = dynamicStateCount;
|
|
dyInfo.pDynamicStates = dynamicStates.data();
|
|
|
|
// Set up multisample state. If sample shading is enabled, assume that
|
|
// we only have one sample enabled, with a non-zero sample mask and no
|
|
// alpha-to-coverage.
|
|
uint32_t msSampleMask = 0x1;
|
|
|
|
VkPipelineMultisampleStateCreateInfo msInfo = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
|
msInfo.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
msInfo.pSampleMask = &msSampleMask;
|
|
msInfo.sampleShadingEnable = VK_TRUE;
|
|
msInfo.minSampleShading = 1.0f;
|
|
|
|
// All depth-stencil state is dynamic, so no need to initialize this.
|
|
// Depth bounds testing is disabled on devices which don't support it.
|
|
VkPipelineDepthStencilStateCreateInfo dsInfo = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
|
|
|
|
// Only the view mask is used as input, and since we do not use MultiView, it is always 0
|
|
VkPipelineRenderingCreateInfo rtInfo = { VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR };
|
|
|
|
VkGraphicsPipelineLibraryCreateInfoEXT libInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT, &rtInfo };
|
|
libInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_FRAGMENT_SHADER_BIT_EXT;
|
|
|
|
VkGraphicsPipelineCreateInfo info = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, &libInfo };
|
|
info.flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
|
|
info.stageCount = stageInfo.getStageCount();
|
|
info.pStages = stageInfo.getStageInfos();
|
|
info.pDepthStencilState = &dsInfo;
|
|
info.pDynamicState = &dyInfo;
|
|
info.layout = m_layout->getPipelineLayout(true);
|
|
info.basePipelineIndex = -1;
|
|
|
|
if (m_shader && m_shader->flags().test(DxvkShaderFlag::HasSampleRateShading))
|
|
info.pMultisampleState = &msInfo;
|
|
|
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
|
|
|
if (vk->vkCreateGraphicsPipelines(vk->device(), m_cache->handle(), 1, &info, nullptr, &pipeline))
|
|
throw DxvkError("DxvkShaderPipelineLibrary: Failed to create compute pipeline");
|
|
|
|
return pipeline;
|
|
}
|
|
|
|
|
|
VkPipeline DxvkShaderPipelineLibrary::compileComputeShaderPipeline() {
|
|
auto vk = m_device->vkd();
|
|
|
|
DxvkShaderStageInfo stageInfo(m_device);
|
|
stageInfo.addStage(VK_SHADER_STAGE_COMPUTE_BIT,
|
|
m_shader->getCode(m_layout, DxvkShaderModuleCreateInfo()), nullptr);
|
|
|
|
// Compile the compute pipeline as normal
|
|
VkComputePipelineCreateInfo info = { VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO };
|
|
info.stage = *stageInfo.getStageInfos();
|
|
info.layout = m_layout->getPipelineLayout(false);
|
|
info.basePipelineIndex = -1;
|
|
|
|
VkPipeline pipeline = VK_NULL_HANDLE;
|
|
|
|
if (vk->vkCreateComputePipelines(vk->device(), m_cache->handle(), 1, &info, nullptr, &pipeline))
|
|
throw DxvkError("DxvkShaderPipelineLibrary: Failed to create compute pipeline");
|
|
|
|
return pipeline;
|
|
}
|
|
|
|
} |