mirror of https://github.com/doitsujin/dxvk
387 lines
12 KiB
C++
387 lines
12 KiB
C++
#include <cstring>
|
|
#include <vector>
|
|
|
|
#include "dxvk_device.h"
|
|
#include "dxvk_descriptor.h"
|
|
#include "dxvk_limits.h"
|
|
#include "dxvk_pipelayout.h"
|
|
|
|
namespace dxvk {
|
|
|
|
uint32_t DxvkBindingInfo::computeSetIndex() const {
|
|
if (stage == VK_SHADER_STAGE_COMPUTE_BIT) {
|
|
// Use one single set for compute shaders
|
|
return DxvkDescriptorSets::CsAll;
|
|
} else if (stage == VK_SHADER_STAGE_FRAGMENT_BIT) {
|
|
// For fragment shaders, create a separate set for UBOs
|
|
if (descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER
|
|
|| descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER)
|
|
return DxvkDescriptorSets::FsBuffers;
|
|
|
|
return DxvkDescriptorSets::FsViews;
|
|
} else {
|
|
// Put all vertex shader resources into the last set.
|
|
// Vertex shader UBOs are usually updated every draw,
|
|
// and other resource types are rarely used.
|
|
return DxvkDescriptorSets::VsAll;
|
|
}
|
|
}
|
|
|
|
|
|
uint32_t DxvkBindingInfo::value() const {
|
|
return (uint32_t(descriptorType) << 24) | resourceBinding;
|
|
}
|
|
|
|
|
|
bool DxvkBindingInfo::eq(const DxvkBindingInfo& other) const {
|
|
return descriptorType == other.descriptorType
|
|
&& resourceBinding == other.resourceBinding
|
|
&& viewType == other.viewType
|
|
&& stage == other.stage
|
|
&& access == other.access;
|
|
}
|
|
|
|
|
|
size_t DxvkBindingInfo::hash() const {
|
|
DxvkHashState hash;
|
|
hash.add(uint32_t(descriptorType));
|
|
hash.add(resourceBinding);
|
|
hash.add(uint32_t(viewType));
|
|
hash.add(uint32_t(stage));
|
|
hash.add(access);
|
|
return hash;
|
|
}
|
|
|
|
|
|
DxvkBindingList::DxvkBindingList() {
|
|
|
|
}
|
|
|
|
|
|
DxvkBindingList::~DxvkBindingList() {
|
|
|
|
}
|
|
|
|
|
|
void DxvkBindingList::addBinding(const DxvkBindingInfo& binding) {
|
|
auto iter = m_bindings.begin();
|
|
|
|
while (iter != m_bindings.end() && iter->value() <= binding.value())
|
|
iter++;
|
|
|
|
m_bindings.insert(iter, binding);
|
|
}
|
|
|
|
|
|
void DxvkBindingList::merge(const DxvkBindingList& list) {
|
|
for (const auto& binding : list.m_bindings)
|
|
addBinding(binding);
|
|
}
|
|
|
|
|
|
bool DxvkBindingList::eq(const DxvkBindingList& other) const {
|
|
if (getBindingCount() != other.getBindingCount())
|
|
return false;
|
|
|
|
for (uint32_t i = 0; i < getBindingCount(); i++) {
|
|
if (!getBinding(i).eq(other.getBinding(i)))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
size_t DxvkBindingList::hash() const {
|
|
DxvkHashState hash;
|
|
|
|
for (const auto& binding : m_bindings)
|
|
hash.add(binding.hash());
|
|
|
|
return hash;
|
|
}
|
|
|
|
|
|
DxvkBindingSetLayoutKey::DxvkBindingSetLayoutKey(const DxvkBindingList& list) {
|
|
m_bindings.resize(list.getBindingCount());
|
|
|
|
for (uint32_t i = 0; i < list.getBindingCount(); i++) {
|
|
m_bindings[i].descriptorType = list.getBinding(i).descriptorType;
|
|
m_bindings[i].stages = list.getBinding(i).stage;
|
|
}
|
|
}
|
|
|
|
|
|
DxvkBindingSetLayoutKey::~DxvkBindingSetLayoutKey() {
|
|
|
|
}
|
|
|
|
|
|
bool DxvkBindingSetLayoutKey::eq(const DxvkBindingSetLayoutKey& other) const {
|
|
if (m_bindings.size() != other.m_bindings.size())
|
|
return false;
|
|
|
|
for (size_t i = 0; i < m_bindings.size(); i++) {
|
|
if (m_bindings[i].descriptorType != other.m_bindings[i].descriptorType
|
|
|| m_bindings[i].stages != other.m_bindings[i].stages)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
size_t DxvkBindingSetLayoutKey::hash() const {
|
|
DxvkHashState hash;
|
|
|
|
for (size_t i = 0; i < m_bindings.size(); i++) {
|
|
hash.add(m_bindings[i].descriptorType);
|
|
hash.add(m_bindings[i].stages);
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
|
|
DxvkBindingSetLayout::DxvkBindingSetLayout(
|
|
DxvkDevice* device,
|
|
const DxvkBindingSetLayoutKey& key)
|
|
: m_device(device) {
|
|
auto vk = m_device->vkd();
|
|
|
|
std::vector<VkDescriptorSetLayoutBinding> bindingInfos;
|
|
std::vector<VkDescriptorUpdateTemplateEntry> templateInfos;
|
|
|
|
bindingInfos.reserve(key.getBindingCount());
|
|
templateInfos.reserve(key.getBindingCount());
|
|
|
|
for (uint32_t i = 0; i < key.getBindingCount(); i++) {
|
|
auto entry = key.getBinding(i);
|
|
|
|
VkDescriptorSetLayoutBinding bindingInfo;
|
|
bindingInfo.binding = i;
|
|
bindingInfo.descriptorType = entry.descriptorType;
|
|
bindingInfo.descriptorCount = 1;
|
|
bindingInfo.stageFlags = entry.stages;
|
|
bindingInfo.pImmutableSamplers = nullptr;
|
|
bindingInfos.push_back(bindingInfo);
|
|
|
|
VkDescriptorUpdateTemplateEntry templateInfo;
|
|
templateInfo.dstBinding = i;
|
|
templateInfo.dstArrayElement = 0;
|
|
templateInfo.descriptorCount = 1;
|
|
templateInfo.descriptorType = entry.descriptorType;
|
|
templateInfo.offset = sizeof(DxvkDescriptorInfo) * i;
|
|
templateInfo.stride = sizeof(DxvkDescriptorInfo);
|
|
templateInfos.push_back(templateInfo);
|
|
}
|
|
|
|
VkDescriptorSetLayoutCreateInfo layoutInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
|
layoutInfo.bindingCount = bindingInfos.size();
|
|
layoutInfo.pBindings = bindingInfos.data();
|
|
|
|
if (vk->vkCreateDescriptorSetLayout(vk->device(), &layoutInfo, nullptr, &m_layout) != VK_SUCCESS)
|
|
throw DxvkError("DxvkBindingSetLayoutKey: Failed to create descriptor set layout");
|
|
|
|
if (layoutInfo.bindingCount) {
|
|
VkDescriptorUpdateTemplateCreateInfo templateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO };
|
|
templateInfo.descriptorUpdateEntryCount = templateInfos.size();
|
|
templateInfo.pDescriptorUpdateEntries = templateInfos.data();
|
|
templateInfo.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET;
|
|
templateInfo.descriptorSetLayout = m_layout;
|
|
|
|
if (vk->vkCreateDescriptorUpdateTemplate(vk->device(), &templateInfo, nullptr, &m_template) != VK_SUCCESS)
|
|
throw DxvkError("DxvkBindingLayoutObjects: Failed to create descriptor update template");
|
|
}
|
|
}
|
|
|
|
|
|
DxvkBindingSetLayout::~DxvkBindingSetLayout() {
|
|
auto vk = m_device->vkd();
|
|
|
|
vk->vkDestroyDescriptorSetLayout(vk->device(), m_layout, nullptr);
|
|
vk->vkDestroyDescriptorUpdateTemplate(vk->device(), m_template, nullptr);
|
|
}
|
|
|
|
|
|
DxvkBindingLayout::DxvkBindingLayout(VkShaderStageFlags stages)
|
|
: m_pushConst { 0, 0, 0 }, m_stages(stages) {
|
|
|
|
}
|
|
|
|
|
|
DxvkBindingLayout::~DxvkBindingLayout() {
|
|
|
|
}
|
|
|
|
|
|
uint32_t DxvkBindingLayout::getSetMask() const {
|
|
uint32_t mask = 0;
|
|
|
|
if (m_stages & VK_SHADER_STAGE_COMPUTE_BIT)
|
|
mask |= (1u << DxvkDescriptorSets::CsAll);
|
|
|
|
if (m_stages & VK_SHADER_STAGE_FRAGMENT_BIT) {
|
|
mask |= (1u << DxvkDescriptorSets::FsViews)
|
|
| (1u << DxvkDescriptorSets::FsBuffers);
|
|
}
|
|
|
|
if (m_stages & VK_SHADER_STAGE_VERTEX_BIT)
|
|
mask |= (1u << DxvkDescriptorSets::VsAll);
|
|
|
|
return mask;
|
|
}
|
|
|
|
|
|
void DxvkBindingLayout::addBinding(const DxvkBindingInfo& binding) {
|
|
uint32_t set = binding.computeSetIndex();
|
|
m_bindings[set].addBinding(binding);
|
|
}
|
|
|
|
|
|
void DxvkBindingLayout::addPushConstantRange(VkPushConstantRange range) {
|
|
uint32_t oldEnd = m_pushConst.offset + m_pushConst.size;
|
|
uint32_t newEnd = range.offset + range.size;
|
|
|
|
m_pushConst.stageFlags |= range.stageFlags;
|
|
m_pushConst.offset = std::min(m_pushConst.offset, range.offset);
|
|
m_pushConst.size = std::max(oldEnd, newEnd) - m_pushConst.offset;
|
|
}
|
|
|
|
|
|
void DxvkBindingLayout::merge(const DxvkBindingLayout& layout) {
|
|
for (uint32_t i = 0; i < layout.m_bindings.size(); i++)
|
|
m_bindings[i].merge(layout.m_bindings[i]);
|
|
|
|
addPushConstantRange(layout.m_pushConst);
|
|
}
|
|
|
|
|
|
bool DxvkBindingLayout::eq(const DxvkBindingLayout& other) const {
|
|
if (m_stages != other.m_stages)
|
|
return false;
|
|
|
|
for (uint32_t i = 0; i < m_bindings.size(); i++) {
|
|
if (!m_bindings[i].eq(other.m_bindings[i]))
|
|
return false;
|
|
}
|
|
|
|
if (m_pushConst.stageFlags != other.m_pushConst.stageFlags
|
|
|| m_pushConst.offset != other.m_pushConst.offset
|
|
|| m_pushConst.size != other.m_pushConst.size)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
size_t DxvkBindingLayout::hash() const {
|
|
DxvkHashState hash;
|
|
hash.add(m_stages);
|
|
|
|
for (uint32_t i = 0; i < m_bindings.size(); i++)
|
|
hash.add(m_bindings[i].hash());
|
|
|
|
hash.add(m_pushConst.stageFlags);
|
|
hash.add(m_pushConst.offset);
|
|
hash.add(m_pushConst.size);
|
|
return hash;
|
|
}
|
|
|
|
|
|
DxvkBindingLayoutObjects::DxvkBindingLayoutObjects(
|
|
DxvkDevice* device,
|
|
const DxvkBindingLayout& layout,
|
|
const DxvkBindingSetLayout** setObjects)
|
|
: m_device(device), m_layout(layout) {
|
|
auto vk = m_device->vkd();
|
|
|
|
std::array<VkDescriptorSetLayout, DxvkDescriptorSets::SetCount> setLayouts = { };
|
|
|
|
// Use minimum number of sets for the given pipeline layout type
|
|
uint32_t setCount = m_layout.getStages() == VK_SHADER_STAGE_COMPUTE_BIT
|
|
? DxvkDescriptorSets::CsSetCount
|
|
: DxvkDescriptorSets::SetCount;
|
|
|
|
for (uint32_t i = 0; i < setCount; i++) {
|
|
m_bindingObjects[i] = setObjects[i];
|
|
|
|
// Sets can be null for partial layouts
|
|
if (setObjects[i]) {
|
|
setLayouts[i] = setObjects[i]->getSetLayout();
|
|
|
|
uint32_t bindingCount = m_layout.getBindingCount(i);
|
|
|
|
for (uint32_t j = 0; j < bindingCount; j++) {
|
|
const DxvkBindingInfo& binding = m_layout.getBinding(i, j);
|
|
|
|
DxvkBindingKey key;
|
|
key.stage = binding.stage;
|
|
key.binding = binding.resourceBinding;
|
|
|
|
DxvkBindingMapping mapping;
|
|
mapping.set = i;
|
|
mapping.binding = j;
|
|
|
|
m_mapping.insert({ key, mapping });
|
|
}
|
|
|
|
if (bindingCount) {
|
|
m_bindingCount += bindingCount;
|
|
m_setMask |= 1u << i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create pipeline layout objects
|
|
VkPushConstantRange pushConst = m_layout.getPushConstantRange();
|
|
|
|
VkPipelineLayoutCreateInfo pipelineLayoutInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
|
pipelineLayoutInfo.setLayoutCount = setCount;
|
|
pipelineLayoutInfo.pSetLayouts = setLayouts.data();
|
|
|
|
if (pushConst.stageFlags && pushConst.size) {
|
|
pipelineLayoutInfo.pushConstantRangeCount = 1;
|
|
pipelineLayoutInfo.pPushConstantRanges = &pushConst;
|
|
}
|
|
|
|
// If the full set is defined, create a layout without INDEPENDENT_SET_BITS
|
|
if (m_layout.getSetMask() == (1u << setCount) - 1) {
|
|
if (vk->vkCreatePipelineLayout(vk->device(), &pipelineLayoutInfo, nullptr, &m_completeLayout))
|
|
throw DxvkError("DxvkBindingLayoutObjects: Failed to create pipeline layout");
|
|
}
|
|
|
|
// If graphics pipeline libraries are supported, also create a variand with the
|
|
// bit. It will be used to create shader-based libraries and link pipelines.
|
|
if (m_device->canUseGraphicsPipelineLibrary() && (m_layout.getStages() & VK_SHADER_STAGE_ALL_GRAPHICS)) {
|
|
pipelineLayoutInfo.flags = VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT;
|
|
|
|
if (vk->vkCreatePipelineLayout(vk->device(), &pipelineLayoutInfo, nullptr, &m_independentLayout))
|
|
throw DxvkError("DxvkBindingLayoutObjects: Failed to create pipeline layout");
|
|
}
|
|
}
|
|
|
|
|
|
DxvkBindingLayoutObjects::~DxvkBindingLayoutObjects() {
|
|
auto vk = m_device->vkd();
|
|
|
|
vk->vkDestroyPipelineLayout(vk->device(), m_completeLayout, nullptr);
|
|
vk->vkDestroyPipelineLayout(vk->device(), m_independentLayout, nullptr);
|
|
}
|
|
|
|
|
|
DxvkGlobalPipelineBarrier DxvkBindingLayoutObjects::getGlobalBarrier() const {
|
|
DxvkGlobalPipelineBarrier barrier = { };
|
|
|
|
for (uint32_t i = 0; i < DxvkDescriptorSets::SetCount; i++) {
|
|
for (uint32_t j = 0; j < m_layout.getBindingCount(i); j++) {
|
|
const auto& binding = m_layout.getBinding(i, j);
|
|
barrier.stages |= util::pipelineStages(binding.stage);
|
|
barrier.access |= binding.access;
|
|
}
|
|
}
|
|
|
|
return barrier;
|
|
}
|
|
|
|
} |