1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2025-02-20 10:54:16 +01:00

[dxvk] Re-implemented pipeline creation within the backend

This commit is contained in:
Philip Rebohle 2017-12-07 09:38:31 +01:00
parent e95dc64c77
commit 19851c8432
24 changed files with 612 additions and 196 deletions

View File

@ -9,7 +9,7 @@ exe_wrapper = 'wine'
c_args = ['-Og', '-ggdb']
c_link_args = ['-static', '-static-libgcc']
cpp_args = ['-std=c++17', '-Og', '-ggdb']
cpp_args = ['-std=c++17', '-Og', '-gstabs']
cpp_link_args = ['-static', '-static-libgcc', '-static-libstdc++']
[host_machine]

View File

@ -55,8 +55,6 @@ namespace dxvk {
// Set up context state. The shader bindings and the
// constant state objects will never be modified.
m_context->bindGraphicsPipeline(createPipeline());
m_context->setInputAssemblyState(
new DxvkInputAssemblyState(
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP,
@ -111,6 +109,14 @@ namespace dxvk {
new DxvkBlendState(
VK_FALSE, VK_LOGIC_OP_NO_OP,
1, &blendAttachment));
m_context->bindShader(
VK_SHADER_STAGE_VERTEX_BIT,
this->createVertexShader());
m_context->bindShader(
VK_SHADER_STAGE_FRAGMENT_BIT,
this->createFragmentShader());
}
@ -329,7 +335,8 @@ namespace dxvk {
// Create the actual shader module
return m_device->createShader(
VK_SHADER_STAGE_VERTEX_BIT, module.compile());
VK_SHADER_STAGE_VERTEX_BIT,
0, nullptr, module.compile());
}
@ -397,42 +404,24 @@ namespace dxvk {
module.opReturn();
module.functionEnd();
// Register function entry point
std::array<uint32_t, 2> interfaces = { inTexCoord, outColor };
module.addEntryPoint(entryPointId, spv::ExecutionModelFragment,
"main", interfaces.size(), interfaces.data());
// Shader resource slots
std::array<DxvkResourceSlot, 2> resourceSlots = {{
{ BindingIds::Sampler, VK_DESCRIPTOR_TYPE_SAMPLER },
{ BindingIds::Texture, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE },
}};
// Create the actual shader module
return m_device->createShader(
VK_SHADER_STAGE_FRAGMENT_BIT, module.compile());
}
Rc<DxvkBindingLayout> DxgiPresenter::createBindingLayout() {
std::array<DxvkDescriptorSlot, 2> bindings;
bindings.at(BindingIds::Sampler).slot = BindingIds::Sampler;
bindings.at(BindingIds::Sampler).type = VK_DESCRIPTOR_TYPE_SAMPLER;
bindings.at(BindingIds::Sampler).stages = VK_SHADER_STAGE_FRAGMENT_BIT;
bindings.at(BindingIds::Texture).slot = BindingIds::Texture;
bindings.at(BindingIds::Texture).type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
bindings.at(BindingIds::Texture).stages = VK_SHADER_STAGE_FRAGMENT_BIT;
return m_device->createBindingLayout(
bindings.size(), bindings.data());
}
Rc<DxvkGraphicsPipeline> DxgiPresenter::createPipeline() {
const Rc<DxvkShader> vs = this->createVertexShader();
const Rc<DxvkShader> fs = this->createFragmentShader();
return m_device->createGraphicsPipeline(
this->createBindingLayout(),
vs, nullptr, nullptr, nullptr, fs);
VK_SHADER_STAGE_FRAGMENT_BIT,
resourceSlots.size(),
resourceSlots.data(),
module.compile());
}
}

View File

@ -76,10 +76,6 @@ namespace dxvk {
Rc<DxvkShader> createVertexShader();
Rc<DxvkShader> createFragmentShader();
Rc<DxvkBindingLayout> createBindingLayout();
Rc<DxvkGraphicsPipeline> createPipeline();
};
}

View File

@ -336,6 +336,7 @@ namespace dxvk {
DxvkImageCreateInfo imageInfo;
imageInfo.type = VK_IMAGE_TYPE_2D;
imageInfo.format = bufferFormat.actual;
imageInfo.flags = VK_IMAGE_CREATE_MUTABLE_FORMAT_BIT;
imageInfo.sampleCount = VK_SAMPLE_COUNT_1_BIT;
imageInfo.extent.width = m_desc.BufferDesc.Width;
imageInfo.extent.height = m_desc.BufferDesc.Height;

View File

@ -4,7 +4,7 @@
#include "dxvk_descriptor.h"
#include "dxvk_lifetime.h"
#include "dxvk_pipeline.h"
#include "dxvk_pipelayout.h"
namespace dxvk {

View File

@ -3,17 +3,36 @@
namespace dxvk {
DxvkComputePipeline::DxvkComputePipeline(
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkBindingLayout>& layout,
const Rc<DxvkShader>& cs)
: m_vkd(vkd), m_layout(layout), m_cs(cs) {
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkShader>& cs)
: m_vkd(vkd) {
DxvkDescriptorSlotMapping slotMapping;
cs->defineResourceSlots(slotMapping);
m_layout = new DxvkBindingLayout(vkd,
slotMapping.bindingCount(),
slotMapping.bindingInfos());
m_cs = cs->createShaderModule(slotMapping);
this->compilePipeline();
}
DxvkComputePipeline::~DxvkComputePipeline() {
if (m_pipeline != VK_NULL_HANDLE)
m_vkd->vkDestroyPipeline(m_vkd->device(), m_pipeline, nullptr);
}
void DxvkComputePipeline::compilePipeline() {
std::vector<VkDescriptorSetLayoutBinding> bindings;
VkComputePipelineCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
info.stage = cs->stageInfo();
info.stage = m_cs->stageInfo();
info.layout = m_layout->pipelineLayout();
info.basePipelineHandle = VK_NULL_HANDLE;
info.basePipelineIndex = 0;
@ -23,10 +42,4 @@ namespace dxvk {
throw DxvkError("DxvkComputePipeline::DxvkComputePipeline: Failed to compile pipeline");
}
DxvkComputePipeline::~DxvkComputePipeline() {
if (m_pipeline != VK_NULL_HANDLE)
m_vkd->vkDestroyPipeline(m_vkd->device(), m_pipeline, nullptr);
}
}

View File

@ -1,6 +1,6 @@
#pragma once
#include "dxvk_pipeline.h"
#include "dxvk_pipelayout.h"
#include "dxvk_resource.h"
#include "dxvk_shader.h"
@ -19,9 +19,8 @@ namespace dxvk {
public:
DxvkComputePipeline(
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkBindingLayout>& layout,
const Rc<DxvkShader>& cs);
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkShader>& cs);
~DxvkComputePipeline();
/**
@ -48,10 +47,12 @@ namespace dxvk {
Rc<vk::DeviceFn> m_vkd;
Rc<DxvkBindingLayout> m_layout;
Rc<DxvkShader> m_cs;
Rc<DxvkShaderModule> m_cs;
VkPipeline m_pipeline = VK_NULL_HANDLE;
void compilePipeline();
};
}

View File

@ -53,32 +53,6 @@ namespace dxvk {
}
void DxvkContext::bindComputePipeline(
const Rc<DxvkComputePipeline>& pipeline) {
if (m_state.cPipe != pipeline) {
m_state.cPipe = pipeline;
m_flags.set(
DxvkContextFlag::CpDirtyPipeline,
DxvkContextFlag::CpDirtyResources);
}
}
void DxvkContext::bindGraphicsPipeline(
const Rc<DxvkGraphicsPipeline>& pipeline) {
if (m_state.gPipe != pipeline) {
m_state.gPipe = pipeline;
m_flags.set(
DxvkContextFlag::GpDirtyPipeline,
DxvkContextFlag::GpDirtyResources,
DxvkContextFlag::GpDirtyVertexBuffers,
DxvkContextFlag::GpDirtyIndexBuffer);
}
}
void DxvkContext::bindIndexBuffer(
const DxvkBufferBinding& buffer) {
if (m_state.vi.indexBuffer != buffer) {
@ -178,6 +152,31 @@ namespace dxvk {
}
void DxvkContext::bindShader(
VkShaderStageFlagBits stage,
const Rc<DxvkShader>& shader) {
DxvkShaderStage* shaderStage = nullptr;
switch (stage) {
case VK_SHADER_STAGE_VERTEX_BIT: shaderStage = &m_state.gp.vs; break;
case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT: shaderStage = &m_state.gp.tcs; break;
case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT: shaderStage = &m_state.gp.tes; break;
case VK_SHADER_STAGE_GEOMETRY_BIT: shaderStage = &m_state.gp.gs; break;
case VK_SHADER_STAGE_FRAGMENT_BIT: shaderStage = &m_state.gp.fs; break;
case VK_SHADER_STAGE_COMPUTE_BIT: shaderStage = &m_state.cp.cs; break;
default: return;
}
if (shaderStage->shader != shader) {
shaderStage->shader = shader;
m_flags.set(stage == VK_SHADER_STAGE_COMPUTE_BIT
? DxvkContextFlag::CpDirtyPipeline
: DxvkContextFlag::GpDirtyPipeline);
}
}
void DxvkContext::bindVertexBuffer(
uint32_t binding,
const DxvkBufferBinding& buffer) {
@ -445,9 +444,12 @@ namespace dxvk {
if (m_flags.test(DxvkContextFlag::CpDirtyPipeline)) {
m_flags.clr(DxvkContextFlag::CpDirtyPipeline);
m_state.cp.pipeline = m_device->createComputePipeline(
m_state.cp.cs.shader);
m_cmd->cmdBindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE,
m_state.cPipe->getPipelineHandle());
m_cmd->trackResource(m_state.cPipe);
m_state.cp.pipeline->getPipelineHandle());
m_cmd->trackResource(m_state.cp.pipeline);
}
}
@ -456,6 +458,10 @@ namespace dxvk {
if (m_flags.test(DxvkContextFlag::GpDirtyPipeline)) {
m_flags.clr(DxvkContextFlag::GpDirtyPipeline);
m_state.gp.pipeline = m_device->createGraphicsPipeline(
m_state.gp.vs.shader, m_state.gp.tcs.shader, m_state.gp.tes.shader,
m_state.gp.gs.shader, m_state.gp.fs.shader);
DxvkGraphicsPipelineStateInfo gpState;
gpState.inputAssemblyState = m_state.co.inputAssemblyState;
gpState.inputLayout = m_state.co.inputLayout;
@ -467,8 +473,8 @@ namespace dxvk {
gpState.viewportCount = m_state.vp.viewportCount;
m_cmd->cmdBindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS,
m_state.gPipe->getPipelineHandle(gpState));
m_cmd->trackResource(m_state.gPipe);
m_state.gp.pipeline->getPipelineHandle(gpState));
m_cmd->trackResource(m_state.gp.pipeline);
}
}
@ -477,7 +483,7 @@ namespace dxvk {
if (m_flags.test(DxvkContextFlag::CpDirtyResources)) {
m_flags.clr(DxvkContextFlag::CpDirtyResources);
auto layout = m_state.cPipe->layout();
auto layout = m_state.cp.pipeline->layout();
m_cmd->bindResourceDescriptors(
VK_PIPELINE_BIND_POINT_COMPUTE,
@ -494,7 +500,7 @@ namespace dxvk {
if (m_flags.test(DxvkContextFlag::GpDirtyResources)) {
m_flags.clr(DxvkContextFlag::GpDirtyResources);
auto layout = m_state.gPipe->layout();
auto layout = m_state.gp.pipeline->layout();
m_cmd->bindResourceDescriptors(
VK_PIPELINE_BIND_POINT_GRAPHICS,

View File

@ -54,20 +54,6 @@ namespace dxvk {
void bindFramebuffer(
const Rc<DxvkFramebuffer>& fb);
/**
* \brief Binds compute pipeline
* \param [in] pipeline The pipeline to bind
*/
void bindComputePipeline(
const Rc<DxvkComputePipeline>& pipeline);
/**
* \brief Binds graphics pipeline
* \param [in] pipeline The pipeline to bind
*/
void bindGraphicsPipeline(
const Rc<DxvkGraphicsPipeline>& pipeline);
/**
* \brief Binds index buffer
*
@ -133,6 +119,16 @@ namespace dxvk {
uint32_t slot,
const Rc<DxvkSampler>& sampler);
/**
* \brief Binds a shader to a given state
*
* \param [in] stage Target shader stage
* \param [in] shader The shader to bind
*/
void bindShader(
VkShaderStageFlagBits stage,
const Rc<DxvkShader>& shader);
/**
* \brief Binds vertex buffer
*

View File

@ -7,7 +7,7 @@
#include "dxvk_graphics.h"
#include "dxvk_image.h"
#include "dxvk_limits.h"
#include "dxvk_pipeline.h"
#include "dxvk_pipelayout.h"
#include "dxvk_sampler.h"
#include "dxvk_shader.h"
@ -54,6 +54,28 @@ namespace dxvk {
};
struct DxvkShaderStage {
Rc<DxvkShader> shader;
};
struct DxvkGraphicsPipelineState {
DxvkShaderStage vs;
DxvkShaderStage tcs;
DxvkShaderStage tes;
DxvkShaderStage gs;
DxvkShaderStage fs;
Rc<DxvkGraphicsPipeline> pipeline;
};
struct DxvkComputePipelineState {
DxvkShaderStage cs;
Rc<DxvkComputePipeline> pipeline;
};
/**
* \brief Pipeline state
*
@ -66,8 +88,8 @@ namespace dxvk {
DxvkOutputMergerState om;
DxvkConstantStateObjects co;
Rc<DxvkGraphicsPipeline> gPipe;
Rc<DxvkComputePipeline> cPipe;
DxvkGraphicsPipelineState gp;
DxvkComputePipelineState cp;
};
}

View File

@ -11,7 +11,8 @@ namespace dxvk {
m_vkd (vkd),
m_features (features),
m_memory (new DxvkMemoryAllocator(adapter, vkd)),
m_renderPassPool (new DxvkRenderPassPool (vkd)) {
m_renderPassPool (new DxvkRenderPassPool (vkd)),
m_pipelineManager (new DxvkPipelineManager(vkd)) {
m_vkd->vkGetDeviceQueue(m_vkd->device(),
m_adapter->graphicsQueueFamily(), 0,
&m_graphicsQueue);
@ -23,6 +24,7 @@ namespace dxvk {
DxvkDevice::~DxvkDevice() {
m_renderPassPool = nullptr;
m_pipelineManager = nullptr;
m_memory = nullptr;
m_vkd->vkDeviceWaitIdle(m_vkd->device());
@ -92,34 +94,27 @@ namespace dxvk {
Rc<DxvkShader> DxvkDevice::createShader(
VkShaderStageFlagBits stage,
uint32_t slotCount,
const DxvkResourceSlot* slotInfos,
const SpirvCodeBuffer& code) {
return new DxvkShader(m_vkd, stage, code);
}
Rc<DxvkBindingLayout> DxvkDevice::createBindingLayout(
uint32_t bindingCount,
const DxvkDescriptorSlot* bindingInfos) {
return new DxvkBindingLayout(m_vkd, bindingCount, bindingInfos);
return new DxvkShader(m_vkd, stage,
slotCount, slotInfos, code);
}
Rc<DxvkComputePipeline> DxvkDevice::createComputePipeline(
const Rc<DxvkBindingLayout>& layout,
const Rc<DxvkShader>& cs) {
return new DxvkComputePipeline(m_vkd, layout, cs);
const Rc<DxvkShader>& cs) {
return m_pipelineManager->createComputePipeline(cs);
}
Rc<DxvkGraphicsPipeline> DxvkDevice::createGraphicsPipeline(
const Rc<DxvkBindingLayout>& layout,
const Rc<DxvkShader>& vs,
const Rc<DxvkShader>& tcs,
const Rc<DxvkShader>& tes,
const Rc<DxvkShader>& gs,
const Rc<DxvkShader>& fs) {
return new DxvkGraphicsPipeline(m_vkd,
layout, vs, tcs, tes, gs, fs);
const Rc<DxvkShader>& vs,
const Rc<DxvkShader>& tcs,
const Rc<DxvkShader>& tes,
const Rc<DxvkShader>& gs,
const Rc<DxvkShader>& fs) {
return m_pipelineManager->createGraphicsPipeline(vs, tcs, tes, gs, fs);
}

View File

@ -8,6 +8,7 @@
#include "dxvk_framebuffer.h"
#include "dxvk_image.h"
#include "dxvk_memory.h"
#include "dxvk_pipemanager.h"
#include "dxvk_renderpass.h"
#include "dxvk_sampler.h"
#include "dxvk_shader.h"
@ -166,48 +167,36 @@ namespace dxvk {
*/
Rc<DxvkShader> createShader(
VkShaderStageFlagBits stage,
uint32_t slotCount,
const DxvkResourceSlot* slotInfos,
const SpirvCodeBuffer& code);
/**
* \brief Creates binding layout
*
* \param [in] bindingCount Number of bindings
* \param [in] bindingInfos Binding descriptions
* \returns New binding layout
*/
Rc<DxvkBindingLayout> createBindingLayout(
uint32_t bindingCount,
const DxvkDescriptorSlot* bindingInfos);
/**
* \brief Creates a compute pipeline
* \brief Retrieves a compute pipeline
*
* \param [in] layout Pipeline binding layout
* \param [in] cs Compute shader
* \returns New compute pipeline
* \returns The compute pipeline
*/
Rc<DxvkComputePipeline> createComputePipeline(
const Rc<DxvkBindingLayout>& layout,
const Rc<DxvkShader>& cs);
const Rc<DxvkShader>& cs);
/**
* \brief Creates a graphics pipeline
* \brief Retrieves a graphics pipeline object
*
* \param [in] layout Pipeline binding layout
* \param [in] vs Vertex shader
* \param [in] tcs Tessellation control shader
* \param [in] tes Tessellation evaluation shader
* \param [in] gs Geometry shader
* \param [in] fs Fragment shader
* \returns New graphics pipeline
* \returns The graphics pipeline
*/
Rc<DxvkGraphicsPipeline> createGraphicsPipeline(
const Rc<DxvkBindingLayout>& layout,
const Rc<DxvkShader>& vs,
const Rc<DxvkShader>& tcs,
const Rc<DxvkShader>& tes,
const Rc<DxvkShader>& gs,
const Rc<DxvkShader>& fs);
const Rc<DxvkShader>& vs,
const Rc<DxvkShader>& tcs,
const Rc<DxvkShader>& tes,
const Rc<DxvkShader>& gs,
const Rc<DxvkShader>& fs);
/**
* \brief Creates a swap chain
@ -252,6 +241,7 @@ namespace dxvk {
Rc<DxvkMemoryAllocator> m_memory;
Rc<DxvkRenderPassPool> m_renderPassPool;
Rc<DxvkPipelineManager> m_pipelineManager;
VkQueue m_graphicsQueue;
VkQueue m_presentQueue;

View File

@ -39,16 +39,29 @@ namespace dxvk {
DxvkGraphicsPipeline::DxvkGraphicsPipeline(
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkBindingLayout>& layout,
const Rc<DxvkShader>& vs,
const Rc<DxvkShader>& tcs,
const Rc<DxvkShader>& tes,
const Rc<DxvkShader>& gs,
const Rc<DxvkShader>& fs)
: m_vkd(vkd), m_layout(layout),
m_vs(vs), m_tcs(tcs), m_tes(tes), m_gs(gs), m_fs(fs) {
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkShader>& vs,
const Rc<DxvkShader>& tcs,
const Rc<DxvkShader>& tes,
const Rc<DxvkShader>& gs,
const Rc<DxvkShader>& fs)
: m_vkd(vkd) {
DxvkDescriptorSlotMapping slotMapping;
if (vs != nullptr) vs ->defineResourceSlots(slotMapping);
if (tcs != nullptr) tcs->defineResourceSlots(slotMapping);
if (tes != nullptr) tes->defineResourceSlots(slotMapping);
if (gs != nullptr) gs ->defineResourceSlots(slotMapping);
if (fs != nullptr) fs ->defineResourceSlots(slotMapping);
m_layout = new DxvkBindingLayout(vkd,
slotMapping.bindingCount(),
slotMapping.bindingInfos());
if (vs != nullptr) m_vs = vs ->createShaderModule(slotMapping);
if (tcs != nullptr) m_tcs = tcs->createShaderModule(slotMapping);
if (tes != nullptr) m_tes = tes->createShaderModule(slotMapping);
if (gs != nullptr) m_gs = gs ->createShaderModule(slotMapping);
if (fs != nullptr) m_fs = fs ->createShaderModule(slotMapping);
}

View File

@ -5,7 +5,7 @@
#include "dxvk_constant_state.h"
#include "dxvk_hash.h"
#include "dxvk_pipeline.h"
#include "dxvk_pipelayout.h"
#include "dxvk_resource.h"
#include "dxvk_shader.h"
@ -49,13 +49,12 @@ namespace dxvk {
public:
DxvkGraphicsPipeline(
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkBindingLayout>& layout,
const Rc<DxvkShader>& vs,
const Rc<DxvkShader>& tcs,
const Rc<DxvkShader>& tes,
const Rc<DxvkShader>& gs,
const Rc<DxvkShader>& fs);
const Rc<vk::DeviceFn>& vkd,
const Rc<DxvkShader>& vs,
const Rc<DxvkShader>& tcs,
const Rc<DxvkShader>& tes,
const Rc<DxvkShader>& gs,
const Rc<DxvkShader>& fs);
~DxvkGraphicsPipeline();
/**
@ -82,11 +81,11 @@ namespace dxvk {
Rc<vk::DeviceFn> m_vkd;
Rc<DxvkBindingLayout> m_layout;
Rc<DxvkShader> m_vs;
Rc<DxvkShader> m_tcs;
Rc<DxvkShader> m_tes;
Rc<DxvkShader> m_gs;
Rc<DxvkShader> m_fs;
Rc<DxvkShaderModule> m_vs;
Rc<DxvkShaderModule> m_tcs;
Rc<DxvkShaderModule> m_tes;
Rc<DxvkShaderModule> m_gs;
Rc<DxvkShaderModule> m_fs;
std::mutex m_mutex;

View File

@ -1,9 +1,44 @@
#include <cstring>
#include "dxvk_pipeline.h"
#include "dxvk_pipelayout.h"
namespace dxvk {
DxvkDescriptorSlotMapping:: DxvkDescriptorSlotMapping() { }
DxvkDescriptorSlotMapping::~DxvkDescriptorSlotMapping() { }
void DxvkDescriptorSlotMapping::defineSlot(
uint32_t slot,
VkDescriptorType type,
VkShaderStageFlagBits stage) {
uint32_t bindingId = this->getBindingId(slot);
if (bindingId != InvalidBinding) {
m_descriptorSlots.at(bindingId).stages |= stage;
} else {
DxvkDescriptorSlot slotInfo;
slotInfo.slot = slot;
slotInfo.type = type;
slotInfo.stages = stage;
m_descriptorSlots.push_back(slotInfo);
}
}
uint32_t DxvkDescriptorSlotMapping::getBindingId(uint32_t slot) {
// This won't win a performance competition, but the number
// of bindings used by a shader is usually much smaller than
// the number of resource slots available to the system.
for (uint32_t i = 0; i < m_descriptorSlots.size(); i++) {
if (m_descriptorSlots.at(i).slot == slot)
return i;
}
return InvalidBinding;
}
DxvkBindingLayout::DxvkBindingLayout(
const Rc<vk::DeviceFn>& vkd,
uint32_t bindingCount,

View File

@ -1,5 +1,7 @@
#pragma once
#include <vector>
#include "dxvk_include.h"
namespace dxvk {
@ -18,6 +20,69 @@ namespace dxvk {
};
/**
* \brief Descriptor slot mapping
*
* Convenience class that generates descriptor slot
* index to binding index mappings. This is required
* when generating Vulkan pipeline and descriptor set
* layouts.
*/
class DxvkDescriptorSlotMapping {
constexpr static uint32_t InvalidBinding = 0xFFFFFFFFu;
public:
DxvkDescriptorSlotMapping();
~DxvkDescriptorSlotMapping();
/**
* \brief Number of descriptor bindings
* \returns Descriptor binding count
*/
uint32_t bindingCount() const {
return m_descriptorSlots.size();
}
/**
* \brief Descriptor binding infos
* \returns Descriptor binding infos
*/
const DxvkDescriptorSlot* bindingInfos() const {
return m_descriptorSlots.data();
}
/**
* \brief Defines a new slot
*
* Adds a slot to the mapping. If the slot is already
* defined by another shader stage, this will extend
* the stage mask by the given stage. Otherwise, an
* entirely new binding is added.
* \param [in] slot Resource slot
* \param [in] type Resource type
* \param [in] stage Shader stage
*/
void defineSlot(
uint32_t slot,
VkDescriptorType type,
VkShaderStageFlagBits stage);
/**
* \brief Gets binding ID for a slot
*
* \param [in] slot Resource slot
* \returns Binding index, or \c InvalidBinding
*/
uint32_t getBindingId(
uint32_t slot);
private:
std::vector<DxvkDescriptorSlot> m_descriptorSlots;
};
/**
* \brief Shader interface
*

View File

@ -0,0 +1,96 @@
#include "dxvk_pipemanager.h"
namespace dxvk {
size_t DxvkPipelineKeyHash::operator () (const DxvkComputePipelineKey& key) const {
std::hash<DxvkShader*> hash;
return hash(key.cs.ptr());
}
size_t DxvkPipelineKeyHash::operator () (const DxvkGraphicsPipelineKey& key) const {
DxvkHashState state;
std::hash<DxvkShader*> hash;
state.add(hash(key.vs.ptr()));
state.add(hash(key.tcs.ptr()));
state.add(hash(key.tes.ptr()));
state.add(hash(key.gs.ptr()));
state.add(hash(key.fs.ptr()));
return state;
}
bool DxvkPipelineKeyEq::operator () (const DxvkComputePipelineKey& a, const DxvkComputePipelineKey& b) const {
return a.cs == b.cs;
}
bool DxvkPipelineKeyEq::operator () (const DxvkGraphicsPipelineKey& a, const DxvkGraphicsPipelineKey& b) const {
return a.vs == b.vs
&& a.tcs == b.tcs
&& a.tes == b.tes
&& a.gs == b.gs
&& a.fs == b.fs;
}
DxvkPipelineManager::DxvkPipelineManager(const Rc<vk::DeviceFn>& vkd)
: m_vkd(vkd) { }
DxvkPipelineManager::~DxvkPipelineManager() {
}
Rc<DxvkComputePipeline> DxvkPipelineManager::createComputePipeline(
const Rc<DxvkShader>& cs) {
if (cs == nullptr)
return nullptr;
DxvkComputePipelineKey key;
key.cs = cs;
std::lock_guard<std::mutex> lock(m_mutex);
auto pair = m_computePipelines.find(key);
if (pair != m_computePipelines.end())
return pair->second;
const Rc<DxvkComputePipeline> pipeline
= new DxvkComputePipeline(m_vkd, cs);
m_computePipelines.insert(std::make_pair(key, pipeline));
return pipeline;
}
Rc<DxvkGraphicsPipeline> DxvkPipelineManager::createGraphicsPipeline(
const Rc<DxvkShader>& vs,
const Rc<DxvkShader>& tcs,
const Rc<DxvkShader>& tes,
const Rc<DxvkShader>& gs,
const Rc<DxvkShader>& fs) {
if (vs == nullptr)
return nullptr;
DxvkGraphicsPipelineKey key;
key.vs = vs;
key.tcs = tcs;
key.tes = tes;
key.gs = gs;
key.fs = fs;
std::lock_guard<std::mutex> lock(m_mutex);
auto pair = m_graphicsPipelines.find(key);
if (pair != m_graphicsPipelines.end())
return pair->second;
const Rc<DxvkGraphicsPipeline> pipeline
= new DxvkGraphicsPipeline(m_vkd, vs, tcs, tes, gs, fs);
m_graphicsPipelines.insert(std::make_pair(key, pipeline));
return pipeline;
}
}

118
src/dxvk/dxvk_pipemanager.h Normal file
View File

@ -0,0 +1,118 @@
#pragma once
#include <mutex>
#include <unordered_map>
#include "dxvk_compute.h"
#include "dxvk_graphics.h"
namespace dxvk {
/**
* \brief Compute pipeline key
*
* Identifier for a compute pipeline object.
* Consists of the compute shader itself.
*/
struct DxvkComputePipelineKey {
Rc<DxvkShader> cs;
};
/**
* \brief Graphics pipeline key
*
* Identifier for a graphics pipeline object.
* Consists of all graphics pipeline shaders.
*/
struct DxvkGraphicsPipelineKey {
Rc<DxvkShader> vs;
Rc<DxvkShader> tcs;
Rc<DxvkShader> tes;
Rc<DxvkShader> gs;
Rc<DxvkShader> fs;
};
struct DxvkPipelineKeyHash {
size_t operator () (const DxvkComputePipelineKey& key) const;
size_t operator () (const DxvkGraphicsPipelineKey& key) const;
};
struct DxvkPipelineKeyEq {
bool operator () (const DxvkComputePipelineKey& a, const DxvkComputePipelineKey& b) const;
bool operator () (const DxvkGraphicsPipelineKey& a, const DxvkGraphicsPipelineKey& b) const;
};
/**
* \brief Pipeline manager
*
* Creates and stores graphics pipelines and compute
* pipelines for each combination of shaders that is
* used within the application. This is necessary
* because DXVK does not expose the concept of shader
* pipeline objects to the client API.
*/
class DxvkPipelineManager : public RcObject {
public:
DxvkPipelineManager(
const Rc<vk::DeviceFn>& vkd);
~DxvkPipelineManager();
/**
* \brief Retrieves a compute pipeline object
*
* If a pipeline for the given shader stage object
* already exists, it will be returned. Otherwise,
* a new pipeline will be created.
* \param [in] cs Compute shader
* \returns Compute pipeline object
*/
Rc<DxvkComputePipeline> createComputePipeline(
const Rc<DxvkShader>& cs);
/**
* \brief Retrieves a graphics pipeline object
*
* If a pipeline for the given shader stage objects
* already exists, it will be returned. Otherwise,
* a new pipeline will be created.
* \param [in] vs Vertex shader
* \param [in] tcs Tessellation control shader
* \param [in] tes Tessellation evaluation shader
* \param [in] gs Geometry shader
* \param [in] fs Fragment shader
* \returns Graphics pipeline object
*/
Rc<DxvkGraphicsPipeline> createGraphicsPipeline(
const Rc<DxvkShader>& vs,
const Rc<DxvkShader>& tcs,
const Rc<DxvkShader>& tes,
const Rc<DxvkShader>& gs,
const Rc<DxvkShader>& fs);
private:
const Rc<vk::DeviceFn> m_vkd;
std::mutex m_mutex;
std::unordered_map<
DxvkComputePipelineKey,
Rc<DxvkComputePipeline>,
DxvkPipelineKeyHash,
DxvkPipelineKeyEq> m_computePipelines;
std::unordered_map<
DxvkGraphicsPipelineKey,
Rc<DxvkGraphicsPipeline>,
DxvkPipelineKeyHash,
DxvkPipelineKeyEq> m_graphicsPipelines;
};
}

View File

@ -2,7 +2,7 @@
namespace dxvk {
DxvkShader::DxvkShader(
DxvkShaderModule::DxvkShaderModule(
const Rc<vk::DeviceFn>& vkd,
VkShaderStageFlagBits stage,
const SpirvCodeBuffer& code)
@ -20,13 +20,13 @@ namespace dxvk {
}
DxvkShader::~DxvkShader() {
DxvkShaderModule::~DxvkShaderModule() {
m_vkd->vkDestroyShaderModule(
m_vkd->device(), m_module, nullptr);
}
VkPipelineShaderStageCreateInfo DxvkShader::stageInfo() const {
VkPipelineShaderStageCreateInfo DxvkShaderModule::stageInfo() const {
VkPipelineShaderStageCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
@ -39,4 +39,35 @@ namespace dxvk {
return info;
}
DxvkShader::DxvkShader(
const Rc<vk::DeviceFn>& vkd,
VkShaderStageFlagBits stage,
uint32_t slotCount,
const DxvkResourceSlot* slotInfos,
const SpirvCodeBuffer& code)
: m_vkd(vkd), m_stage(stage), m_code(code) {
for (uint32_t i = 0; i < slotCount; i++)
m_slots.push_back(slotInfos[i]);
}
DxvkShader::~DxvkShader() {
}
void DxvkShader::defineResourceSlots(
DxvkDescriptorSlotMapping& mapping) const {
for (const auto& slot : m_slots)
mapping.defineSlot(slot.slot, slot.type, m_stage);
}
Rc<DxvkShaderModule> DxvkShader::createShaderModule(
const DxvkDescriptorSlotMapping& mapping) const {
// TODO apply mapping
return new DxvkShaderModule(m_vkd, m_stage, m_code);
}
}

View File

@ -3,6 +3,7 @@
#include <vector>
#include "dxvk_include.h"
#include "dxvk_pipelayout.h"
#include "../spirv/spirv_code_buffer.h"
@ -15,30 +16,31 @@ namespace dxvk {
* binding that a shader can access.
*/
struct DxvkResourceSlot {
uint32_t binding;
uint32_t slot;
VkDescriptorType type;
};
/**
* \brief Shader module
* \brief Shader module object
*
* Manages a Vulkan shader module. This will not
* perform any sort of shader compilation. Instead,
* the context will create pipeline objects on the
* perform any shader compilation. Instead, the
* context will create pipeline objects on the
* fly when executing draw calls.
*/
class DxvkShader : public RcObject {
class DxvkShaderModule : public RcObject {
public:
DxvkShader(
DxvkShaderModule(
const Rc<vk::DeviceFn>& vkd,
VkShaderStageFlagBits stage,
const SpirvCodeBuffer& code);
~DxvkShader();
VkShaderModule module() const {
~DxvkShaderModule();
VkShaderModule handle() const {
return m_module;
}
@ -52,4 +54,51 @@ namespace dxvk {
};
/**
* \brief Shader object
*
* Stores a SPIR-V shader and information on the
* bindings that the shader uses. In order to use
* the shader with a pipeline, a shader module
* needs to be created from he shader object.
*/
class DxvkShader : public RcObject {
public:
DxvkShader(
const Rc<vk::DeviceFn>& vkd,
VkShaderStageFlagBits stage,
uint32_t slotCount,
const DxvkResourceSlot* slotInfos,
const SpirvCodeBuffer& code);
~DxvkShader();
/**
* \brief
*/
void defineResourceSlots(
DxvkDescriptorSlotMapping& mapping) const;
/**
* \brief Creates a shader module
*
* Maps the binding slot numbers
* \param [in] mapping Resource slot mapping
* \returns The shader module
*/
Rc<DxvkShaderModule> createShaderModule(
const DxvkDescriptorSlotMapping& mapping) const;
private:
Rc<vk::DeviceFn> m_vkd;
VkShaderStageFlagBits m_stage;
SpirvCodeBuffer m_code;
std::vector<DxvkResourceSlot> m_slots;
};
}

View File

@ -16,7 +16,8 @@ dxvk_src = files([
'dxvk_lifetime.cpp',
'dxvk_main.cpp',
'dxvk_memory.cpp',
'dxvk_pipeline.cpp',
'dxvk_pipelayout.cpp',
'dxvk_pipemanager.cpp',
'dxvk_renderpass.cpp',
'dxvk_resource.cpp',
'dxvk_sampler.cpp',

View File

@ -29,7 +29,7 @@ namespace dxvk {
*/
spv::Op opCode() const {
return static_cast<spv::Op>(
m_code[0] & spv::OpCodeMask);
this->arg(0) & spv::OpCodeMask);
}
/**
@ -37,7 +37,7 @@ namespace dxvk {
* \returns Number of DWORDs
*/
uint32_t length() const {
return m_code[0] >> spv::WordCountShift;
return this->arg(0) >> spv::WordCountShift;
}
/**

View File

@ -113,18 +113,12 @@ public:
0, nullptr));
m_dxvkVertexShader = m_dxvkDevice->createShader(
VK_SHADER_STAGE_VERTEX_BIT,
VK_SHADER_STAGE_VERTEX_BIT, 0, nullptr,
SpirvCodeBuffer(_countof(vsCode), vsCode));
m_dxvkFragmentShader = m_dxvkDevice->createShader(
VK_SHADER_STAGE_FRAGMENT_BIT,
VK_SHADER_STAGE_FRAGMENT_BIT, 0, nullptr,
SpirvCodeBuffer(_countof(fsCode), fsCode));
m_dxvkBindingLayout = m_dxvkDevice->createBindingLayout(0, nullptr);
m_dxvkPipeline = m_dxvkDevice->createGraphicsPipeline(m_dxvkBindingLayout,
m_dxvkVertexShader, nullptr, nullptr, nullptr, m_dxvkFragmentShader);
m_dxvkContext->bindGraphicsPipeline(m_dxvkPipeline);
}
~TriangleApp() {
@ -158,6 +152,14 @@ public:
m_dxvkContext->setViewports(1, &viewport, &scissor);
m_dxvkContext->bindShader(
VK_SHADER_STAGE_VERTEX_BIT,
m_dxvkVertexShader);
m_dxvkContext->bindShader(
VK_SHADER_STAGE_FRAGMENT_BIT,
m_dxvkFragmentShader);
VkClearAttachment clearAttachment;
clearAttachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
clearAttachment.colorAttachment = 0;
@ -198,10 +200,8 @@ private:
Rc<DxvkSwapchain> m_dxvkSwapchain;
Rc<DxvkContext> m_dxvkContext;
Rc<DxvkShader> m_dxvkVertexShader;
Rc<DxvkShader> m_dxvkFragmentShader;
Rc<DxvkBindingLayout> m_dxvkBindingLayout;
Rc<DxvkGraphicsPipeline> m_dxvkPipeline;
Rc<DxvkShader> m_dxvkVertexShader;
Rc<DxvkShader> m_dxvkFragmentShader;
};

View File

@ -1,6 +1,6 @@
lib_d3d11 = dxvk_compiler.find_library('d3d11')
lib_dxgi = dxvk_compiler.find_library('dxgi')
lib_d3dcompiler_47 = dxvk_compiler.find_library('d3dcompiler_47')
lib_d3dcompiler_47 = dxvk_compiler.find_library('d3dcompiler')
subdir('d3d11')
subdir('dxbc')