port pileline from dxvk

This commit is contained in:
Asuka 2022-04-05 13:31:12 +08:00
parent f4c58dd31f
commit 6fd50f00a3
24 changed files with 3734 additions and 65 deletions

View file

@ -15,69 +15,67 @@
#include "MurmurHash2.h"
namespace algo
{;
namespace alg
{
#define BIG_CONSTANT(x) (x##LLU)
// Just a random 64 bit prime number.
constexpr uint64_t MmhSeed = 0x6FA9DE0959342D4B;
// Just a random 64 bit prime number.
constexpr uint64_t MmhSeed = 0x6FA9DE0959342D4B;
uint64_t MurmurHash64A(const void* key, int len, uint64_t seed)
{
const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995);
const int r = 47;
uint64_t h = seed ^ (len * m);
const uint64_t* data = (const uint64_t*)key;
const uint64_t* end = data + (len / 8);
while (data != end)
uint64_t MurmurHash64A(const void* key, int len, uint64_t seed)
{
uint64_t k = *data++;
const uint64_t m = BIG_CONSTANT(0xc6a4a7935bd1e995);
const int r = 47;
k *= m;
k ^= k >> r;
k *= m;
uint64_t h = seed ^ (len * m);
h ^= k;
const uint64_t* data = (const uint64_t*)key;
const uint64_t* end = data + (len / 8);
while (data != end)
{
uint64_t k = *data++;
k *= m;
k ^= k >> r;
k *= m;
h ^= k;
h *= m;
}
const unsigned char* data2 = (const unsigned char*)data;
switch (len & 7)
{
case 7:
h ^= uint64_t(data2[6]) << 48;
case 6:
h ^= uint64_t(data2[5]) << 40;
case 5:
h ^= uint64_t(data2[4]) << 32;
case 4:
h ^= uint64_t(data2[3]) << 24;
case 3:
h ^= uint64_t(data2[2]) << 16;
case 2:
h ^= uint64_t(data2[1]) << 8;
case 1:
h ^= uint64_t(data2[0]);
h *= m;
};
h ^= h >> r;
h *= m;
h ^= h >> r;
return h;
}
const unsigned char* data2 = (const unsigned char*)data;
switch (len & 7)
uint64_t MurmurHash(const void* key, int len)
{
case 7:
h ^= uint64_t(data2[6]) << 48;
case 6:
h ^= uint64_t(data2[5]) << 40;
case 5:
h ^= uint64_t(data2[4]) << 32;
case 4:
h ^= uint64_t(data2[3]) << 24;
case 3:
h ^= uint64_t(data2[2]) << 16;
case 2:
h ^= uint64_t(data2[1]) << 8;
case 1:
h ^= uint64_t(data2[0]);
h *= m;
};
return MurmurHash64A(key, len, MmhSeed);
}
h ^= h >> r;
h *= m;
h ^= h >> r;
return h;
}
uint64_t MurmurHash(const void* key, int len)
{
return MurmurHash64A(key, len, MmhSeed);
}
} // namespace algo
} // namespace alg

View file

@ -2,11 +2,11 @@
#include "GPCS4Common.h"
namespace algo
{;
namespace alg
{
uint64_t MurmurHash(const void* key, int len);
uint64_t MurmurHash(const void* key, int len);
uint64_t MurmurHash64A(const void* key, int len, uint64_t seed);
uint64_t MurmurHash64A(const void* key, int len, uint64_t seed);
} // namespace algo
} // namespace alg

View file

@ -1,7 +1,7 @@
#include "sha1.h"
#include "Sha1Hash.h"
namespace algo
namespace alg
{
std::string Sha1Hash::toString() const

View file

@ -4,7 +4,7 @@
#include <cstring>
#include <string>
namespace algo {
namespace alg {
using Sha1Digest = std::array<uint8_t, 20>;

View file

@ -74,12 +74,14 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltBarrier.h" />
<ClInclude Include="Graphics\Violet\VltBindMask.h" />
<ClInclude Include="Graphics\Violet\VltBuffer.h" />
<ClInclude Include="Graphics\Violet\VltCmdList.h" />
<ClInclude Include="Graphics\Violet\VltCommon.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltCompute.h" />
<ClInclude Include="Graphics\Violet\VltContext.h" />
<ClInclude Include="Graphics\Violet\VltDebugUtil.h" />
<ClInclude Include="Graphics\Violet\VltDescriptor.h" />
@ -91,6 +93,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltFormat.h" />
<ClInclude Include="Graphics\Violet\VltGraphics.h" />
<ClInclude Include="Graphics\Violet\VltHash.h" />
<ClInclude Include="Graphics\Violet\VltImage.h" />
<ClInclude Include="Graphics\Violet\VltInstance.h">
@ -98,9 +101,12 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltLifetime.h" />
<ClInclude Include="Graphics\Violet\VltLimit.h" />
<ClInclude Include="Graphics\Violet\VltLog.h" />
<ClInclude Include="Graphics\Violet\VltMemory.h" />
<ClInclude Include="Graphics\Violet\VltObject.h" />
<ClInclude Include="Graphics\Violet\VltPipeLayout.h" />
<ClInclude Include="Graphics\Violet\VltPipeManager.h" />
<ClInclude Include="Graphics\Violet\VltQueue.h" />
<ClInclude Include="Graphics\Violet\VltRc.h">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
@ -108,6 +114,9 @@
</ClInclude>
<ClInclude Include="Graphics\Violet\VltRecycler.h" />
<ClInclude Include="Graphics\Violet\VltResource.h" />
<ClInclude Include="Graphics\Violet\VltShader.h" />
<ClInclude Include="Graphics\Violet\VltShaderKey.h" />
<ClInclude Include="Graphics\Violet\VltState.h" />
<ClInclude Include="Graphics\Violet\VltUtil.h" />
<ClInclude Include="Graphics\VirtualGPU.h" />
<ClInclude Include="Loader\elf-sce.h" />
@ -272,6 +281,7 @@
<ClCompile Include="Graphics\Violet\VltBarrier.cpp" />
<ClCompile Include="Graphics\Violet\VltBuffer.cpp" />
<ClCompile Include="Graphics\Violet\VltCmdList.cpp" />
<ClCompile Include="Graphics\Violet\VltCompute.cpp" />
<ClCompile Include="Graphics\Violet\VltContext.cpp" />
<ClCompile Include="Graphics\Violet\VltDebugUtil.cpp" />
<ClCompile Include="Graphics\Violet\VltDescriptor.cpp" />
@ -282,6 +292,7 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="Graphics\Violet\VltFormat.cpp" />
<ClCompile Include="Graphics\Violet\VltGraphics.cpp" />
<ClCompile Include="Graphics\Violet\VltImage.cpp" />
<ClCompile Include="Graphics\Violet\VltInstance.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</ExcludedFromBuild>
@ -290,8 +301,12 @@
<ClCompile Include="Graphics\Violet\VltLifetime.cpp" />
<ClCompile Include="Graphics\Violet\VltLog.cpp" />
<ClCompile Include="Graphics\Violet\VltMemory.cpp" />
<ClCompile Include="Graphics\Violet\VltPipeLayout.cpp" />
<ClCompile Include="Graphics\Violet\VltPipeManager.cpp" />
<ClCompile Include="Graphics\Violet\VltQueue.cpp" />
<ClCompile Include="Graphics\Violet\VltResource.cpp" />
<ClCompile Include="Graphics\Violet\VltShader.cpp" />
<ClCompile Include="Graphics\Violet\VltShaderKey.cpp" />
<ClCompile Include="Graphics\Violet\VltUtil.cpp" />
<ClCompile Include="Graphics\VirtualGPU.cpp" />
<ClCompile Include="ImportLibs.cpp" />
@ -473,11 +488,11 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>$(ProjectDir);$(ProjectDir)Common;$(ProjectDir)Platform;$(ProjectDir)Util;$(ProjectDir)Graphics;$(ProjectDir)SceModules;$(SolutionDir)3rdParty;$(SolutionDir)3rdParty\zydis\include;$(SolutionDir)3rdParty\zydis\dependencies\zycore\include;$(IncludePath)</IncludePath>
<IncludePath>$(ProjectDir);$(ProjectDir)Algorithm;$(ProjectDir)Common;$(ProjectDir)Platform;$(ProjectDir)Util;$(ProjectDir)Graphics;$(ProjectDir)SceModules;$(SolutionDir)3rdParty;$(SolutionDir)3rdParty\zydis\include;$(SolutionDir)3rdParty\zydis\dependencies\zycore\include;$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>$(ProjectDir);$(ProjectDir)Common;$(ProjectDir)Platform;$(ProjectDir)Util;$(ProjectDir)Graphics;$(ProjectDir)SceModules;$(SolutionDir)3rdParty;$(SolutionDir)3rdParty\zydis\include;$(SolutionDir)3rdParty\zydis\dependencies\zycore\include;$(IncludePath)</IncludePath>
<IncludePath>$(ProjectDir);$(ProjectDir)Algorithm;$(ProjectDir)Common;$(ProjectDir)Platform;$(ProjectDir)Util;$(ProjectDir)Graphics;$(ProjectDir)SceModules;$(SolutionDir)3rdParty;$(SolutionDir)3rdParty\zydis\include;$(SolutionDir)3rdParty\zydis\dependencies\zycore\include;$(IncludePath)</IncludePath>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Label="LLVM" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">

View file

@ -787,6 +787,33 @@
<ClInclude Include="Graphics\Violet\VltObject.h">
<Filter>Source Files\Graphics\Violet</Filter>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltPipeManager.h">
<Filter>Source Files\Graphics\Violet</Filter>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltGraphics.h">
<Filter>Source Files\Graphics\Violet</Filter>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltCompute.h">
<Filter>Source Files\Graphics\Violet</Filter>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltBindMask.h">
<Filter>Source Files\Graphics\Violet</Filter>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltLimit.h">
<Filter>Source Files\Graphics\Violet</Filter>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltState.h">
<Filter>Source Files\Graphics\Violet</Filter>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltShader.h">
<Filter>Source Files\Graphics\Violet</Filter>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltPipeLayout.h">
<Filter>Source Files\Graphics\Violet</Filter>
</ClInclude>
<ClInclude Include="Graphics\Violet\VltShaderKey.h">
<Filter>Source Files\Graphics\Violet</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Loader\EbootObject.cpp">
@ -1365,6 +1392,24 @@
<ClCompile Include="Graphics\Sce\SceResourceTracker.cpp">
<Filter>Source Files\Graphics\Sce</Filter>
</ClCompile>
<ClCompile Include="Graphics\Violet\VltPipeManager.cpp">
<Filter>Source Files\Graphics\Violet</Filter>
</ClCompile>
<ClCompile Include="Graphics\Violet\VltGraphics.cpp">
<Filter>Source Files\Graphics\Violet</Filter>
</ClCompile>
<ClCompile Include="Graphics\Violet\VltCompute.cpp">
<Filter>Source Files\Graphics\Violet</Filter>
</ClCompile>
<ClCompile Include="Graphics\Violet\VltShader.cpp">
<Filter>Source Files\Graphics\Violet</Filter>
</ClCompile>
<ClCompile Include="Graphics\Violet\VltPipeLayout.cpp">
<Filter>Source Files\Graphics\Violet</Filter>
</ClCompile>
<ClCompile Include="Graphics\Violet\VltShaderKey.cpp">
<Filter>Source Files\Graphics\Violet</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<Text Include="Emulator\TLSStub.asm">

View file

@ -0,0 +1,164 @@
#pragma once
#include "VltCommon.h"
#include "VltLimit.h"
namespace sce::vlt
{
/**
* \brief Binding mask
*
* Used to track which resource slots have a compatible
* binding and which ones don't. This is used to set up
* binding-related specialization constants in shaders.
* \tparam N Number of binding slots
*/
template <uint32_t BindingCount>
class VltBindingSet
{
constexpr static uint32_t BitCount = 32;
constexpr static uint32_t IntCount = (BindingCount + BitCount - 1) / BitCount;
public:
/**
* \brief Tests whether a binding is active
*
* \param [in] slot The binding ID
* \returns \c true if the binding is active
*/
bool test(uint32_t slot) const
{
const uint32_t intId = slot / BitCount;
const uint32_t bitId = slot % BitCount;
const uint32_t bitMask = 1u << bitId;
return (m_slots[intId] & bitMask) != 0;
}
/**
* \brief Changes a single binding
*
* \param [in] slot The binding ID
* \param [in] value New binding state
* \returns \c true if the state has changed
*/
bool set(uint32_t slot, bool value)
{
const uint32_t intId = slot / BitCount;
const uint32_t bitId = slot % BitCount;
const uint32_t bitMask = 1u << bitId;
const uint32_t prev = m_slots[intId];
const uint32_t next = value
? prev | bitMask
: prev & ~bitMask;
m_slots[intId] = next;
return prev != next;
}
/**
* \brief Marks a binding as active
*
* \param [in] slot The binding ID
* \returns \c true if the state has changed
*/
bool set(uint32_t slot)
{
return set(slot, true);
}
/**
* \brief Marks a binding as inactive
*
* \param [in] slot The binding ID
* \returns \c true if the state has changed
*/
bool clr(uint32_t slot)
{
return set(slot, false);
}
/**
* \brief Clears binding state
*
* Useful to zero out any bindings
* that are not used by a pipeline.
*/
void clear()
{
for (uint32_t i = 0; i < IntCount; i++)
m_slots[i] = 0;
}
/**
* \brief Enables multiple bindings
* \param [in] n Number of bindings
*/
void setFirst(uint32_t n)
{
for (uint32_t i = 0; i < IntCount; i++)
{
m_slots[i] = n >= BitCount ? ~0u : ~(~0u << n);
n = n >= BitCount ? n - BitCount : 0;
}
}
/**
* \brief Finds next set binding
*
* \param [in] first Fist bit to consider
* \returns Binding ID, or -1 if none was found
*/
int32_t findNext(uint32_t first) const
{
if (unlikely(first >= BindingCount))
return -1;
uint32_t intId = first / BitCount;
uint32_t bitId = first % BitCount;
auto mask = m_slots[intId] & ~((1 << bitId) - 1);
while (!mask && ++intId < IntCount)
mask = m_slots[intId];
if (!mask)
return -1;
return BitCount * intId + bit::tzcnt(mask);
}
bool operator==(const VltBindingSet& other) const
{
bool eq = true;
for (uint32_t i = 0; i < IntCount; i++)
eq &= m_slots[i] == other.m_slots[i];
return eq;
}
bool operator!=(const VltBindingSet& other) const
{
return !this->operator==(other);
}
private:
uint32_t m_slots[IntCount];
};
using VltBindingMask = VltBindingSet<MaxNumActiveBindings>;
/**
* \brief Bound shader resources
*
* Stores the resources bound to a binding
* slot in DXVK. These are used to create
* descriptor sets.
*/
//struct DxvkShaderResourceSlot
//{
// Rc<DxvkSampler> sampler;
// Rc<DxvkImageView> imageView;
// Rc<DxvkBufferView> bufferView;
// DxvkBufferSlice bufferSlice;
//};
} // namespace sce::vlt

View file

@ -0,0 +1,124 @@
#include "VltCompute.h"
#include "VltDevice.h"
#include "VltPipeManager.h"
namespace sce::vlt
{
VltComputePipeline::VltComputePipeline(
VltPipelineManager* pipeMgr,
VltComputePipelineShaders shaders) :
m_device(pipeMgr->m_device),
m_pipeMgr(pipeMgr),
m_shaders(std::move(shaders))
{
m_shaders.cs->defineResourceSlots(m_slotMapping);
m_layout = new VltPipelineLayout(m_device,
m_slotMapping, VK_PIPELINE_BIND_POINT_COMPUTE);
}
VltComputePipeline::~VltComputePipeline()
{
for (const auto& instance : m_pipelines)
this->destroyPipeline(instance.pipeline());
}
VkPipeline VltComputePipeline::getPipelineHandle(
const VltComputePipelineStateInfo& state)
{
VltComputePipelineInstance* instance = nullptr;
{
std::lock_guard<util::sync::Spinlock> lock(m_mutex);
instance = this->findInstance(state);
if (instance)
return instance->pipeline();
// If no pipeline instance exists with the given state
// vector, create a new one and add it to the list.
instance = this->createInstance(state);
}
if (!instance)
return VK_NULL_HANDLE;
return instance->pipeline();
}
void VltComputePipeline::compilePipeline(
const VltComputePipelineStateInfo& state)
{
std::lock_guard<util::sync::Spinlock> lock(m_mutex);
if (!this->findInstance(state))
this->createInstance(state);
}
VltComputePipelineInstance* VltComputePipeline::createInstance(
const VltComputePipelineStateInfo& state)
{
VkPipeline newPipelineHandle = this->createPipeline(state);
m_pipeMgr->m_numComputePipelines += 1;
return &m_pipelines.emplace_back(state, newPipelineHandle);
}
VltComputePipelineInstance* VltComputePipeline::findInstance(
const VltComputePipelineStateInfo& state)
{
for (auto& instance : m_pipelines)
{
if (instance.isCompatible(state))
return &instance;
}
return nullptr;
}
VkPipeline VltComputePipeline::createPipeline(
const VltComputePipelineStateInfo& state) const
{
std::vector<VkDescriptorSetLayoutBinding> bindings;
if (Logger::logLevel() <= LogLevel::Debug)
{
Logger::debug("Compiling compute pipeline...");
Logger::debug(util::str::formatex(" cs : ", m_shaders.cs->debugName()));
}
DxvkShaderModuleCreateInfo moduleInfo;
moduleInfo.fsDualSrcBlend = false;
auto csm = m_shaders.cs->createShaderModule(m_device, m_slotMapping, moduleInfo);
VkComputePipelineCreateInfo info;
info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
info.pNext = nullptr;
info.flags = 0;
info.stage = csm.stageInfo(nullptr);
info.layout = m_layout->pipelineLayout();
info.basePipelineHandle = VK_NULL_HANDLE;
info.basePipelineIndex = -1;
VkPipeline pipeline = VK_NULL_HANDLE;
if (vkCreateComputePipelines(m_device->handle(),
VK_NULL_HANDLE, 1, &info, nullptr, &pipeline) != VK_SUCCESS)
{
Logger::err("DxvkComputePipeline: Failed to compile pipeline");
Logger::err(util::str::formatex(" cs : ", m_shaders.cs->debugName()));
return VK_NULL_HANDLE;
}
return pipeline;
}
void VltComputePipeline::destroyPipeline(VkPipeline pipeline)
{
vkDestroyPipeline(m_device->handle(), pipeline, nullptr);
}
} // namespace sce::vlt

View file

@ -0,0 +1,160 @@
#pragma once
#include "VltCommon.h"
#include "VltShader.h"
#include "VltState.h"
namespace sce::vlt
{
class VltDevice;
class VltPipelineManager;
/**
* \brief Shaders used in compute pipelines
*/
struct VltComputePipelineShaders
{
Rc<VltShader> cs;
bool eq(const VltComputePipelineShaders& other) const
{
return cs == other.cs;
}
size_t hash() const
{
return VltShader::getHash(cs);
}
};
/**
* \brief Compute pipeline instance
*/
class VltComputePipelineInstance
{
public:
VltComputePipelineInstance() :
m_stateVector(),
m_pipeline(VK_NULL_HANDLE)
{
}
VltComputePipelineInstance(
const VltComputePipelineStateInfo& state,
VkPipeline pipe) :
m_stateVector(state),
m_pipeline(pipe)
{
}
/**
* \brief Checks for matching pipeline state
*
* \param [in] stateVector Graphics pipeline state
* \param [in] renderPass Render pass handle
* \returns \c true if the specialization is compatible
*/
bool isCompatible(const VltComputePipelineStateInfo& state) const
{
return m_stateVector == state;
}
/**
* \brief Retrieves pipeline
* \returns The pipeline handle
*/
VkPipeline pipeline() const
{
return m_pipeline;
}
private:
VltComputePipelineStateInfo m_stateVector;
VkPipeline m_pipeline;
};
/**
* \brief Compute pipeline
*
* Stores a compute pipeline object and the corresponding
* pipeline layout. Unlike graphics pipelines, compute
* pipelines do not need to be recompiled against any sort
* of pipeline state.
*/
class VltComputePipeline
{
public:
VltComputePipeline(
VltPipelineManager* pipeMgr,
VltComputePipelineShaders shaders);
~VltComputePipeline();
/**
* \brief Shaders used by the pipeline
* \returns Shaders used by the pipeline
*/
const VltComputePipelineShaders& shaders() const
{
return m_shaders;
}
/**
* \brief Pipeline layout
*
* Stores the pipeline layout and the descriptor set
* layout, as well as information on the resource
* slots used by the pipeline.
* \returns Pipeline layout
*/
VltPipelineLayout* layout() const
{
return m_layout.ptr();
}
/**
* \brief Retrieves pipeline handle
*
* \param [in] state Pipeline state
* \returns Pipeline handle
*/
VkPipeline getPipelineHandle(
const VltComputePipelineStateInfo& state);
/**
* \brief Compiles a pipeline
*
* Asynchronously compiles the given pipeline
* and stores the result for future use.
* \param [in] state Pipeline state
*/
void compilePipeline(
const VltComputePipelineStateInfo& state);
private:
VltDevice* m_device;
VltPipelineManager* m_pipeMgr;
VltComputePipelineShaders m_shaders;
VltDescriptorSlotMapping m_slotMapping;
Rc<VltPipelineLayout> m_layout;
util::sync::Spinlock m_mutex;
std::vector<VltComputePipelineInstance> m_pipelines;
VltComputePipelineInstance* createInstance(
const VltComputePipelineStateInfo& state);
VltComputePipelineInstance* findInstance(
const VltComputePipelineStateInfo& state);
VkPipeline createPipeline(
const VltComputePipelineStateInfo& state) const;
void destroyPipeline(
VkPipeline pipeline);
};
} // namespace sce::vlt

View file

@ -0,0 +1,521 @@
#include "VltGraphics.h"
#include "VltDevice.h"
#include "VltPipeManager.h"
#include "VltShader.h"
namespace sce::vlt
{
VltGraphicsPipeline::VltGraphicsPipeline(
VltPipelineManager* pipeMgr,
VltGraphicsPipelineShaders shaders) :
m_device(pipeMgr->m_device),
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_vsIn = m_shaders.vs != nullptr ? m_shaders.vs->interfaceSlots().inputSlots : 0;
m_fsOut = m_shaders.fs != nullptr ? m_shaders.fs->interfaceSlots().outputSlots : 0;
m_layout = new VltPipelineLayout(m_device,
m_slotMapping, VK_PIPELINE_BIND_POINT_GRAPHICS);
}
VltGraphicsPipeline::~VltGraphicsPipeline()
{
for (const auto& instance : m_pipelines)
this->destroyPipeline(instance.pipeline());
}
Rc<VltShader> VltGraphicsPipeline::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 VltGraphicsPipeline::getPipelineHandle(
const VltGraphicsPipelineStateInfo& state)
{
VltGraphicsPipelineInstance* instance = nullptr;
{
std::lock_guard<util::sync::Spinlock> lock(m_mutex);
instance = this->findInstance(state);
if (instance)
return instance->pipeline();
instance = this->createInstance(state);
}
if (!instance)
return VK_NULL_HANDLE;
return instance->pipeline();
}
void VltGraphicsPipeline::compilePipeline(
const VltGraphicsPipelineStateInfo& state)
{
std::lock_guard<util::sync::Spinlock> lock(m_mutex);
if (!this->findInstance(state))
this->createInstance(state);
}
VltGraphicsPipelineInstance* VltGraphicsPipeline::createInstance(
const VltGraphicsPipelineStateInfo& state)
{
// 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);
m_pipeMgr->m_numGraphicsPipelines += 1;
return &m_pipelines.emplace_back(state, newPipelineHandle);
}
VltGraphicsPipelineInstance* VltGraphicsPipeline::findInstance(
const VltGraphicsPipelineStateInfo& state)
{
for (auto& instance : m_pipelines)
{
if (instance.isCompatible(state))
return &instance;
}
return nullptr;
}
VkPipeline VltGraphicsPipeline::createPipeline(
const VltGraphicsPipelineStateInfo& state) const
{
if (Logger::logLevel() <= LogLevel::Debug)
{
Logger::debug("Compiling graphics pipeline...");
this->logPipelineState(LogLevel::Debug, state);
}
// 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());
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(nullptr));
if (tcsm)
stages.push_back(tcsm.stageInfo(nullptr));
if (tesm)
stages.push_back(tesm.stageInfo(nullptr));
if (gsm)
stages.push_back(gsm.stageInfo(nullptr));
if (fsm)
stages.push_back(fsm.stageInfo(nullptr));
// 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.cbBlend[i].state();
if (omBlendAttachments[i].colorWriteMask != fullMask)
{
omBlendAttachments[i].colorWriteMask = vutil::remapComponentMask(
state.cbBlend[i].colorWriteMask(), state.cbSwizzle[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(); // &&!vutil::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.cb.enableLogicOp();
cbInfo.logicOp = state.cb.logicOp();
cbInfo.attachmentCount = VltLimits::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 = VK_NULL_HANDLE;
info.subpass = 0;
info.basePipelineHandle = VK_NULL_HANDLE;
info.basePipelineIndex = -1;
if (tsInfo.patchControlPoints == 0)
info.pTessellationState = nullptr;
VkPipeline pipeline = VK_NULL_HANDLE;
if (vkCreateGraphicsPipelines(m_device->handle(),
VK_NULL_HANDLE, 1, &info, nullptr, &pipeline) != VK_SUCCESS)
{
Logger::err("DxvkGraphicsPipeline: Failed to compile pipeline");
this->logPipelineState(LogLevel::Error, state);
return VK_NULL_HANDLE;
}
return pipeline;
}
void VltGraphicsPipeline::destroyPipeline(VkPipeline pipeline) const
{
vkDestroyPipeline(m_device->handle(), pipeline, nullptr);
}
VltShaderModule VltGraphicsPipeline::createShaderModule(
const Rc<VltShader>& shader,
const VltGraphicsPipelineStateInfo& state) const
{
if (shader == nullptr)
return VltShaderModule();
DxvkShaderModuleCreateInfo info;
// Fix up fragment shader outputs for dual-source blending
if (shader->stage() == VK_SHADER_STAGE_FRAGMENT_BIT)
{
info.fsDualSrcBlend = state.cbBlend[0].blendEnable() && (vutil::isDualSourceBlendFactor(state.cbBlend[0].srcColorBlendFactor()) ||
vutil::isDualSourceBlendFactor(state.cbBlend[0].dstColorBlendFactor()) ||
vutil::isDualSourceBlendFactor(state.cbBlend[0].srcAlphaBlendFactor()) ||
vutil::isDualSourceBlendFactor(state.cbBlend[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_device, m_slotMapping, info);
}
Rc<VltShader> VltGraphicsPipeline::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<VltShader> 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 VltGraphicsPipeline::validatePipelineState(
const VltGraphicsPipelineStateInfo& 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() > VltLimits::MaxNumVertexAttributes ||
state.il.bindingCount() > VltLimits::MaxNumVertexBindings)
return false;
// No errors
return true;
}
void VltGraphicsPipeline::logPipelineState(
LogLevel level,
const VltGraphicsPipelineStateInfo& state) const
{
if (m_shaders.vs != nullptr)
Logger::log(level, util::str::formatex(" vs : ", m_shaders.vs->debugName()));
if (m_shaders.tcs != nullptr)
Logger::log(level, util::str::formatex(" tcs : ", m_shaders.tcs->debugName()));
if (m_shaders.tes != nullptr)
Logger::log(level, util::str::formatex(" tes : ", m_shaders.tes->debugName()));
if (m_shaders.gs != nullptr)
Logger::log(level, util::str::formatex(" gs : ", m_shaders.gs->debugName()));
if (m_shaders.fs != nullptr)
Logger::log(level, util::str::formatex(" fs : ", m_shaders.fs->debugName()));
for (uint32_t i = 0; i < state.il.attributeCount(); i++)
{
const auto& attr = state.ilAttributes[i];
Logger::log(level, util::str::formatex(" 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, util::str::formatex(" binding ", i, " : binding ", bind.binding(), ", stride ", bind.stride(), ", rate ", bind.inputRate(), ", divisor ", bind.divisor()));
}
// TODO log more pipeline state
}
} // namespace sce::vlt

View file

@ -0,0 +1,246 @@
#pragma once
#include "VltCommon.h"
#include "VltHash.h"
#include "VltPipeLayout.h"
#include "VltShader.h"
#include "VltState.h"
namespace sce::vlt
{
class VltDevice;
class VltPipelineManager;
/**
* \brief Flags that describe pipeline properties
*/
enum class VltGraphicsPipelineFlag
{
HasTransformFeedback,
HasStorageDescriptors,
};
using VltGraphicsPipelineFlags = util::Flags<VltGraphicsPipelineFlag>;
/**
* \brief Shaders used in graphics pipelines
*/
struct VltGraphicsPipelineShaders
{
Rc<VltShader> vs;
Rc<VltShader> tcs;
Rc<VltShader> tes;
Rc<VltShader> gs;
Rc<VltShader> fs;
bool eq(const VltGraphicsPipelineShaders& other) const
{
return vs == other.vs &&
tcs == other.tcs &&
tes == other.tes &&
gs == other.gs &&
fs == other.fs;
}
size_t hash() const
{
VltHashState state;
state.add(VltShader::getHash(vs));
state.add(VltShader::getHash(tcs));
state.add(VltShader::getHash(tes));
state.add(VltShader::getHash(gs));
state.add(VltShader::getHash(fs));
return state;
}
};
/**
* \brief Common graphics pipeline state
*
* Non-dynamic pipeline state that cannot
* be changed dynamically.
*/
struct VltGraphicsCommonPipelineStateInfo
{
bool msSampleShadingEnable;
float msSampleShadingFactor;
};
/**
* \brief Graphics pipeline instance
*
* Stores a state vector and the
* corresponding pipeline handle.
*/
class VltGraphicsPipelineInstance
{
public:
VltGraphicsPipelineInstance() :
m_stateVector(),
m_pipeline(VK_NULL_HANDLE)
{
}
VltGraphicsPipelineInstance(
const VltGraphicsPipelineStateInfo& state,
VkPipeline pipe) :
m_stateVector(state),
m_pipeline(pipe)
{
}
/**
* \brief Checks for matching pipeline state
*
* \param [in] stateVector Graphics pipeline state
* \returns \c true if the specialization is compatible
*/
bool isCompatible(
const VltGraphicsPipelineStateInfo& state)
{
return m_stateVector == state;
}
/**
* \brief Retrieves pipeline
* \returns The pipeline handle
*/
VkPipeline pipeline() const
{
return m_pipeline;
}
private:
VltGraphicsPipelineStateInfo m_stateVector;
VkPipeline m_pipeline;
};
/**
* \brief Graphics pipeline
*
* Stores the pipeline layout as well as methods to
* recompile the graphics pipeline against a given
* pipeline state vector.
*/
class VltGraphicsPipeline
{
public:
VltGraphicsPipeline(
VltPipelineManager* pipeMgr,
VltGraphicsPipelineShaders shaders);
~VltGraphicsPipeline();
/**
* \brief Shaders used by the pipeline
* \returns Shaders used by the pipeline
*/
const VltGraphicsPipelineShaders& shaders() const
{
return m_shaders;
}
/**
* \brief Returns graphics pipeline flags
* \returns Graphics pipeline property flags
*/
VltGraphicsPipelineFlags flags() const
{
return m_flags;
}
/**
* \brief Pipeline layout
*
* Stores the pipeline layout and the descriptor set
* layout, as well as information on the resource
* slots used by the pipeline.
* \returns Pipeline layout
*/
VltPipelineLayout* layout() const
{
return m_layout.ptr();
}
/**
* \brief Queries shader for a given stage
*
* In case no shader is specified for the
* given stage, \c nullptr will be returned.
* \param [in] stage The shader stage
* \returns Shader of the given stage
*/
Rc<VltShader> getShader(
VkShaderStageFlagBits stage) const;
/**
* \brief Pipeline handle
*
* Retrieves a pipeline handle for the given pipeline
* state. If necessary, a new pipeline will be created.
* \param [in] state Pipeline state vector
* \param [in] renderPass The render pass
* \returns Pipeline handle
*/
VkPipeline getPipelineHandle(
const VltGraphicsPipelineStateInfo& state);
/**
* \brief Compiles a pipeline
*
* Asynchronously compiles the given pipeline
* and stores the result for future use.
* \param [in] state Pipeline state vector
*/
void compilePipeline(
const VltGraphicsPipelineStateInfo& state);
private:
VltDevice* m_device;
VltPipelineManager* m_pipeMgr;
VltGraphicsPipelineShaders m_shaders;
VltDescriptorSlotMapping m_slotMapping;
Rc<VltPipelineLayout> m_layout;
uint32_t m_vsIn = 0;
uint32_t m_fsOut = 0;
VltGraphicsPipelineFlags m_flags;
VltGraphicsCommonPipelineStateInfo m_common;
// List of pipeline instances, shared between threads
alignas(CACHE_LINE_SIZE) util::sync::Spinlock m_mutex;
std::vector<VltGraphicsPipelineInstance> m_pipelines;
VltGraphicsPipelineInstance* createInstance(
const VltGraphicsPipelineStateInfo& state);
VltGraphicsPipelineInstance* findInstance(
const VltGraphicsPipelineStateInfo& state);
VkPipeline createPipeline(
const VltGraphicsPipelineStateInfo& state) const;
void destroyPipeline(
VkPipeline pipeline) const;
VltShaderModule createShaderModule(
const Rc<VltShader>& shader,
const VltGraphicsPipelineStateInfo& state) const;
Rc<VltShader> getPrevStageShader(
VkShaderStageFlagBits stage) const;
bool validatePipelineState(
const VltGraphicsPipelineStateInfo& state) const;
void logPipelineState(
LogLevel level,
const VltGraphicsPipelineStateInfo& state) const;
};
} // namespace sce::vlt

View file

@ -0,0 +1,22 @@
#pragma once
namespace sce::vlt
{
enum VltLimits : size_t
{
MaxNumRenderTargets = 8,
MaxNumVertexAttributes = 32,
MaxNumVertexBindings = 32,
MaxNumXfbBuffers = 4,
MaxNumXfbStreams = 4,
MaxNumViewports = 16,
MaxNumResourceSlots = 1216,
MaxNumActiveBindings = 128,
MaxNumQueuedCommandBuffers = 12,
MaxNumQueryCountPerPool = 128,
MaxNumSpecConstants = 12,
MaxUniformBufferSize = 65536,
MaxVertexBindingStride = 2048,
MaxPushConstantSize = 128,
};
} // namespace sce::vlt

View file

@ -4,6 +4,15 @@ LOG_CHANNEL(Graphic.Violet);
namespace sce::vlt
{
LogLevel Logger::logLevel()
{
return LogLevel::Trace;
}
void Logger::log(LogLevel level, const std::string& message)
{
LOG_DEBUG(message.c_str());
}
void Logger::trace(const std::string& message)
{
@ -35,4 +44,6 @@ namespace sce::vlt
LOG_ASSERT(false, message.c_str());
}
} // namespace sce::vlt

View file

@ -17,6 +17,9 @@ namespace sce::vlt
class Logger
{
public:
static LogLevel logLevel();
static void log(LogLevel level, const std::string& message);
static void trace(const std::string& message);
static void debug(const std::string& message);
static void info(const std::string& message);

View file

@ -0,0 +1,208 @@
#include "VltPipeLayout.h"
#include "VltDescriptor.h"
#include "VltDevice.h"
namespace sce::vlt
{
VltDescriptorSlotMapping::VltDescriptorSlotMapping()
{
}
VltDescriptorSlotMapping::~VltDescriptorSlotMapping()
{
}
void VltDescriptorSlotMapping::defineSlot(
VkShaderStageFlagBits stage,
const VltResourceSlot& desc)
{
uint32_t bindingId = this->getBindingId(desc.slot);
if (bindingId != InvalidBinding)
{
m_descriptorSlots[bindingId].stages |= stage;
m_descriptorSlots[bindingId].access |= desc.access;
}
else
{
DxvkDescriptorSlot slotInfo;
slotInfo.slot = desc.slot;
slotInfo.type = desc.type;
slotInfo.view = desc.view;
slotInfo.stages = stage;
slotInfo.access = desc.access;
m_descriptorSlots.push_back(slotInfo);
}
}
void VltDescriptorSlotMapping::definePushConstRange(
VkShaderStageFlagBits stage,
uint32_t offset,
uint32_t size)
{
m_pushConstRange.stageFlags |= stage;
m_pushConstRange.size = std::max(
m_pushConstRange.size, offset + size);
}
uint32_t VltDescriptorSlotMapping::getBindingId(uint32_t slot) const
{
// 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[i].slot == slot)
return i;
}
return InvalidBinding;
}
void VltDescriptorSlotMapping::makeDescriptorsDynamic(
uint32_t uniformBuffers,
uint32_t storageBuffers)
{
if (this->countDescriptors(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER) <= uniformBuffers)
this->replaceDescriptors(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC);
}
uint32_t VltDescriptorSlotMapping::countDescriptors(
VkDescriptorType type) const
{
uint32_t count = 0;
for (const auto& slot : m_descriptorSlots)
count += slot.type == type ? 1 : 0;
return count;
}
void VltDescriptorSlotMapping::replaceDescriptors(
VkDescriptorType oldType,
VkDescriptorType newType)
{
for (auto& slot : m_descriptorSlots)
{
if (slot.type == oldType)
slot.type = newType;
}
}
VltPipelineLayout::VltPipelineLayout(
VltDevice* device,
const VltDescriptorSlotMapping& slotMapping,
VkPipelineBindPoint pipelineBindPoint) :
m_device(device),
m_pushConstRange(slotMapping.pushConstRange()),
m_bindingSlots(slotMapping.bindingCount())
{
auto bindingCount = slotMapping.bindingCount();
auto bindingInfos = slotMapping.bindingInfos();
for (uint32_t i = 0; i < bindingCount; i++)
m_bindingSlots[i] = bindingInfos[i];
std::vector<VkDescriptorSetLayoutBinding> bindings(bindingCount);
std::vector<VkDescriptorUpdateTemplateEntryKHR> tEntries(bindingCount);
for (uint32_t i = 0; i < bindingCount; i++)
{
bindings[i].binding = i;
bindings[i].descriptorType = bindingInfos[i].type;
bindings[i].descriptorCount = 1;
bindings[i].stageFlags = bindingInfos[i].stages;
bindings[i].pImmutableSamplers = nullptr;
tEntries[i].dstBinding = i;
tEntries[i].dstArrayElement = 0;
tEntries[i].descriptorCount = 1;
tEntries[i].descriptorType = bindingInfos[i].type;
tEntries[i].offset = sizeof(VltDescriptorInfo) * i;
tEntries[i].stride = 0;
if (bindingInfos[i].type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC)
m_dynamicSlots.push_back(i);
m_descriptorTypes.set(bindingInfos[i].type);
}
// Create descriptor set layout. We do not need to
// create one if there are no active resource bindings.
if (bindingCount > 0)
{
VkDescriptorSetLayoutCreateInfo dsetInfo;
dsetInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
dsetInfo.pNext = nullptr;
dsetInfo.flags = 0;
dsetInfo.bindingCount = bindings.size();
dsetInfo.pBindings = bindings.data();
if (vkCreateDescriptorSetLayout(m_device->handle(),
&dsetInfo, nullptr, &m_descriptorSetLayout) != VK_SUCCESS)
Logger::exception("DxvkPipelineLayout: Failed to create descriptor set layout");
}
// Create pipeline layout with the given descriptor set layout
VkPipelineLayoutCreateInfo pipeInfo;
pipeInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeInfo.pNext = nullptr;
pipeInfo.flags = 0;
pipeInfo.setLayoutCount = bindingCount > 0 ? 1 : 0;
pipeInfo.pSetLayouts = &m_descriptorSetLayout;
pipeInfo.pushConstantRangeCount = 0;
pipeInfo.pPushConstantRanges = nullptr;
if (m_pushConstRange.size)
{
pipeInfo.pushConstantRangeCount = 1;
pipeInfo.pPushConstantRanges = &m_pushConstRange;
}
if (vkCreatePipelineLayout(m_device->handle(),
&pipeInfo, nullptr, &m_pipelineLayout) != VK_SUCCESS)
{
vkDestroyDescriptorSetLayout(m_device->handle(), m_descriptorSetLayout, nullptr);
Logger::exception("DxvkPipelineLayout: Failed to create pipeline layout");
}
// Create descriptor update template. If there are no active
// resource bindings, there won't be any descriptors to update.
if (bindingCount > 0)
{
VkDescriptorUpdateTemplateCreateInfoKHR templateInfo;
templateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_CREATE_INFO_KHR;
templateInfo.pNext = nullptr;
templateInfo.flags = 0;
templateInfo.descriptorUpdateEntryCount = tEntries.size();
templateInfo.pDescriptorUpdateEntries = tEntries.data();
templateInfo.templateType = VK_DESCRIPTOR_UPDATE_TEMPLATE_TYPE_DESCRIPTOR_SET_KHR;
templateInfo.descriptorSetLayout = m_descriptorSetLayout;
templateInfo.pipelineBindPoint = pipelineBindPoint;
templateInfo.pipelineLayout = m_pipelineLayout;
templateInfo.set = 0;
if (vkCreateDescriptorUpdateTemplate(
m_device->handle(), &templateInfo, nullptr, &m_descriptorTemplate) != VK_SUCCESS)
{
vkDestroyDescriptorSetLayout(m_device->handle(), m_descriptorSetLayout, nullptr);
vkDestroyPipelineLayout(m_device->handle(), m_pipelineLayout, nullptr);
Logger::exception("DxvkPipelineLayout: Failed to create descriptor update template");
}
}
}
VltPipelineLayout::~VltPipelineLayout()
{
vkDestroyDescriptorUpdateTemplate(
m_device->handle(), m_descriptorTemplate, nullptr);
vkDestroyPipelineLayout(
m_device->handle(), m_pipelineLayout, nullptr);
vkDestroyDescriptorSetLayout(
m_device->handle(), m_descriptorSetLayout, nullptr);
}
} // namespace sce::vlt

View file

@ -0,0 +1,293 @@
#pragma once
#include "VltCommon.h"
namespace sce::vlt
{
class VltDevice;
/**
* \brief Resource slot
*
* Describes the type of a single resource
* binding that a shader can access.
*/
struct VltResourceSlot
{
uint32_t slot;
VkDescriptorType type;
VkImageViewType view;
VkAccessFlags access;
};
/**
* \brief Shader interface binding
*
* Corresponds to a single descriptor binding in
* Vulkan. DXVK does not use descriptor arrays.
* Instead, each binding stores one descriptor.
*/
struct DxvkDescriptorSlot
{
uint32_t slot; ///< Resource slot index for the context
VkDescriptorType type; ///< Descriptor type (aka resource type)
VkImageViewType view; ///< Compatible image view type
VkShaderStageFlags stages; ///< Stages that can use the resource
VkAccessFlags access; ///< Access flags
};
/**
* \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 VltDescriptorSlotMapping
{
constexpr static uint32_t InvalidBinding = 0xFFFFFFFFu;
public:
VltDescriptorSlotMapping();
~VltDescriptorSlotMapping();
/**
* \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 Push constant range
* \returns Push constant range
*/
VkPushConstantRange pushConstRange() const
{
return m_pushConstRange;
}
/**
* \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] stage Shader stage
* \param [in] desc Slot description
*/
void defineSlot(
VkShaderStageFlagBits stage,
const VltResourceSlot& desc);
/**
* \brief Defines new push constant range
*
* \param [in] stage Shader stage
* \param [in] offset Range offset
* \param [in] size Range size
*/
void definePushConstRange(
VkShaderStageFlagBits stage,
uint32_t offset,
uint32_t size);
/**
* \brief Gets binding ID for a slot
*
* \param [in] slot Resource slot
* \returns Binding index, or \c InvalidBinding
*/
uint32_t getBindingId(
uint32_t slot) const;
/**
* \brief Makes static descriptors dynamic
*
* Replaces static uniform and storage buffer bindings by
* their dynamic equivalent if the number of bindings of
* the respective type lies within supported device limits.
* Using dynamic descriptor types may improve performance.
* \param [in] uniformBuffers Max number of uniform buffers
* \param [in] storageBuffers Max number of storage buffers
*/
void makeDescriptorsDynamic(
uint32_t uniformBuffers,
uint32_t storageBuffers);
private:
std::vector<DxvkDescriptorSlot> m_descriptorSlots;
VkPushConstantRange m_pushConstRange = {};
uint32_t countDescriptors(
VkDescriptorType type) const;
void replaceDescriptors(
VkDescriptorType oldType,
VkDescriptorType newType);
};
/**
* \brief Shader interface
*
* Describes shader resource bindings
* for a graphics or compute pipeline.
*/
class VltPipelineLayout : public RcObject
{
public:
VltPipelineLayout(
VltDevice* device,
const VltDescriptorSlotMapping& slotMapping,
VkPipelineBindPoint pipelineBindPoint);
~VltPipelineLayout();
/**
* \brief Number of resource bindings
* \returns Resource binding count
*/
uint32_t bindingCount() const
{
return m_bindingSlots.size();
}
/**
* \brief Resource binding info
*
* \param [in] id Binding index
* \returns Resource binding info
*/
const DxvkDescriptorSlot& binding(uint32_t id) const
{
return m_bindingSlots[id];
}
/**
* \brief Resource binding info
* \returns Resource binding info
*/
const DxvkDescriptorSlot* bindings() const
{
return m_bindingSlots.data();
}
/**
* \brief Push constant range
* \returns Push constant range
*/
const VkPushConstantRange& pushConstRange() const
{
return m_pushConstRange;
}
/**
* \brief Descriptor set layout handle
* \returns Descriptor set layout handle
*/
VkDescriptorSetLayout descriptorSetLayout() const
{
return m_descriptorSetLayout;
}
/**
* \brief Pipeline layout handle
* \returns Pipeline layout handle
*/
VkPipelineLayout pipelineLayout() const
{
return m_pipelineLayout;
}
/**
* \brief Descriptor update template
* \returns Descriptor update template
*/
VkDescriptorUpdateTemplateKHR descriptorTemplate() const
{
return m_descriptorTemplate;
}
/**
* \brief Number of dynamic bindings
* \returns Dynamic binding count
*/
uint32_t dynamicBindingCount() const
{
return m_dynamicSlots.size();
}
/**
* \brief Returns a dynamic binding
*
* \param [in] id Dynamic binding ID
* \returns Reference to that binding
*/
const DxvkDescriptorSlot& dynamicBinding(uint32_t id) const
{
return this->binding(m_dynamicSlots[id]);
}
/**
* \brief Checks for static buffer bindings
*
* Returns \c true if there is at least one
* descriptor of the static uniform buffer
* type.
*/
bool hasStaticBufferBindings() const
{
return m_descriptorTypes.test(
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
}
/**
* \brief Checks whether buffers or images are written to
*
* It is assumed that storage images and buffers
* will be written to if they are present. Used
* for synchronization purposes.
* \param [in] stages Shader stages to check
*/
VkShaderStageFlags getStorageDescriptorStages() const
{
VkShaderStageFlags stages = 0;
for (const auto& slot : m_bindingSlots)
{
if (slot.access & VK_ACCESS_SHADER_WRITE_BIT)
stages |= slot.stages;
}
return stages;
}
private:
VltDevice* m_device;
VkPushConstantRange m_pushConstRange = {};
VkDescriptorSetLayout m_descriptorSetLayout = VK_NULL_HANDLE;
VkPipelineLayout m_pipelineLayout = VK_NULL_HANDLE;
VkDescriptorUpdateTemplateKHR m_descriptorTemplate = VK_NULL_HANDLE;
std::vector<DxvkDescriptorSlot> m_bindingSlots;
std::vector<uint32_t> m_dynamicSlots;
util::Flags<VkDescriptorType> m_descriptorTypes;
};
} // namespace sce::vlt

View file

@ -0,0 +1,60 @@
#include "VltPipeManager.h"
namespace sce::vlt
{
VltPipelineManager::VltPipelineManager(VltDevice* device) :
m_device(device)
{
}
VltPipelineManager::~VltPipelineManager()
{
}
VltComputePipeline* VltPipelineManager::createComputePipeline(
const VltComputePipelineShaders& shaders)
{
if (shaders.cs == nullptr)
return nullptr;
std::lock_guard<std::mutex> lock(m_mutex);
auto pair = m_computePipelines.find(shaders);
if (pair != m_computePipelines.end())
return &pair->second;
auto iter = m_computePipelines.emplace(
std::piecewise_construct,
std::tuple(shaders),
std::tuple(this, shaders));
return &iter.first->second;
}
VltGraphicsPipeline* VltPipelineManager::createGraphicsPipeline(
const VltGraphicsPipelineShaders& shaders)
{
if (shaders.vs == nullptr)
return nullptr;
std::lock_guard<std::mutex> lock(m_mutex);
auto pair = m_graphicsPipelines.find(shaders);
if (pair != m_graphicsPipelines.end())
return &pair->second;
auto iter = m_graphicsPipelines.emplace(
std::piecewise_construct,
std::tuple(shaders),
std::tuple(this, shaders));
return &iter.first->second;
}
VltPipelineCount VltPipelineManager::getPipelineCount() const
{
VltPipelineCount result;
result.numComputePipelines = m_numComputePipelines.load();
result.numGraphicsPipelines = m_numGraphicsPipelines.load();
return result;
}
} // namespace sce::vlt

View file

@ -0,0 +1,99 @@
#pragma once
#include "VltCommon.h"
#include "VltCompute.h"
#include "VltGraphics.h"
#include "VltHash.h"
#include <mutex>
#include <unordered_map>
namespace sce::vlt
{
class VltDevice;
/**
* \brief Pipeline count
*
* Stores number of graphics and
* compute pipelines, individually.
*/
struct VltPipelineCount
{
uint32_t numGraphicsPipelines;
uint32_t numComputePipelines;
};
/**
* \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 VltPipelineManager
{
friend class VltComputePipeline;
friend class VltGraphicsPipeline;
public:
VltPipelineManager(
VltDevice* device);
~VltPipelineManager();
/**
* \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] shaders Shaders for the pipeline
* \returns Compute pipeline object
*/
VltComputePipeline* createComputePipeline(
const VltComputePipelineShaders& shaders);
/**
* \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] shaders Shaders for the pipeline
* \returns Graphics pipeline object
*/
VltGraphicsPipeline* createGraphicsPipeline(
const VltGraphicsPipelineShaders& shaders);
/**
* \brief Retrieves total pipeline count
* \returns Number of compute/graphics pipelines
*/
VltPipelineCount getPipelineCount() const;
private:
VltDevice* m_device;
std::atomic<uint32_t> m_numComputePipelines = { 0 };
std::atomic<uint32_t> m_numGraphicsPipelines = { 0 };
std::mutex m_mutex;
std::unordered_map<
VltComputePipelineShaders,
VltComputePipeline,
VltHash,
VltEq>
m_computePipelines;
std::unordered_map<
VltGraphicsPipelineShaders,
VltGraphicsPipeline,
VltHash,
VltEq>
m_graphicsPipelines;
};
} // namespace sce::vlt

View file

@ -0,0 +1,420 @@
#include "VltShader.h"
#include "UtilBit.h"
#include "VltDevice.h"
#include <algorithm>
#include <unordered_map>
#include <unordered_set>
namespace sce::vlt
{
using namespace pssl;
VltShaderConstData::VltShaderConstData()
{
}
VltShaderConstData::VltShaderConstData(
size_t dwordCount,
const uint32_t* dwordArray) :
m_size(dwordCount),
m_data(new uint32_t[dwordCount])
{
for (size_t i = 0; i < dwordCount; i++)
m_data[i] = dwordArray[i];
}
VltShaderConstData::VltShaderConstData(VltShaderConstData&& other) :
m_size(other.m_size), m_data(other.m_data)
{
other.m_size = 0;
other.m_data = nullptr;
}
VltShaderConstData& VltShaderConstData::operator=(VltShaderConstData&& other)
{
delete[] m_data;
this->m_size = other.m_size;
this->m_data = other.m_data;
other.m_size = 0;
other.m_data = nullptr;
return *this;
}
VltShaderConstData::~VltShaderConstData()
{
delete[] m_data;
}
VltShaderModule::VltShaderModule() :
m_device(nullptr), m_stage()
{
}
VltShaderModule::VltShaderModule(VltShaderModule&& other) :
m_device(other.m_device)
{
this->m_stage = other.m_stage;
other.m_stage = VkPipelineShaderStageCreateInfo();
}
VltShaderModule::VltShaderModule(
VltDevice* device,
const Rc<VltShader>& shader,
const SpirvCodeBuffer& code) :
m_device(device),
m_stage()
{
m_stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
m_stage.pNext = nullptr;
m_stage.flags = 0;
m_stage.stage = shader->stage();
m_stage.module = VK_NULL_HANDLE;
m_stage.pName = "main";
m_stage.pSpecializationInfo = nullptr;
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();
if (vkCreateShaderModule(m_device->handle(), &info, nullptr, &m_stage.module) != VK_SUCCESS)
Logger::exception("DxvkComputePipeline::DxvkComputePipeline: Failed to create shader module");
}
VltShaderModule::~VltShaderModule()
{
if (m_device != nullptr)
{
vkDestroyShaderModule(
m_device->handle(), m_stage.module, nullptr);
}
}
VltShaderModule& VltShaderModule::operator=(VltShaderModule&& other)
{
this->m_device = other.m_device;
this->m_stage = other.m_stage;
other.m_stage = VkPipelineShaderStageCreateInfo();
return *this;
}
VltShader::VltShader(
VkShaderStageFlagBits stage,
uint32_t slotCount,
const VltResourceSlot* slotInfos,
const VltInterfaceSlots& iface,
SpirvCodeBuffer code,
const VltShaderOptions& options,
VltShaderConstData&& constData) :
m_stage(stage),
m_code(code), m_interface(iface),
m_options(options), m_constData(std::move(constData))
{
// Write back resource slot infos
for (uint32_t i = 0; i < slotCount; i++)
m_slots.push_back(slotInfos[i]);
// Gather the offsets where the binding IDs
// are stored so we can quickly remap them.
uint32_t o1VarId = 0;
for (auto ins : code)
{
if (ins.opCode() == spv::OpDecorate)
{
if (ins.arg(2) == spv::DecorationBinding || ins.arg(2) == spv::DecorationSpecId)
m_idOffsets.push_back(ins.offset() + 3);
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);
}
}
}
VltShader::~VltShader()
{
}
void VltShader::defineResourceSlots(
VltDescriptorSlotMapping& mapping) const
{
for (const auto& slot : m_slots)
mapping.defineSlot(m_stage, slot);
if (m_interface.pushConstSize)
{
mapping.definePushConstRange(m_stage,
m_interface.pushConstOffset,
m_interface.pushConstSize);
}
}
VltShaderModule VltShader::createShaderModule(
VltDevice* device,
const VltDescriptorSlotMapping& mapping,
const DxvkShaderModuleCreateInfo& info)
{
SpirvCodeBuffer spirvCode = m_code.decompress();
uint32_t* code = spirvCode.data();
// Remap resource binding IDs
for (uint32_t ofs : m_idOffsets)
{
if (code[ofs] < MaxNumResourceSlots)
code[ofs] = mapping.getBindingId(code[ofs]);
}
// For dual-source blending we need to re-map
// location 1, index 0 to location 0, index 1
if (info.fsDualSrcBlend && m_o1IdxOffset && m_o1LocOffset)
std::swap(code[m_o1IdxOffset], code[m_o1LocOffset]);
// Replace undefined input variables with zero
for (uint32_t u = info.undefinedInputs; u; u &= u - 1)
eliminateInput(spirvCode, util::bit::tzcnt(u));
return VltShaderModule(device, this, spirvCode);
}
void VltShader::dump(std::ostream& outputStream) const
{
m_code.decompress().store(outputStream);
}
void VltShader::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 declarations
for (auto ins : code)
{
if (ins.opCode() == spv::OpDecorate &&
ins.arg(2) == spv::DecorationLocation &&
ins.arg(1) == inputVarId)
{
code.beginInsertion(ins.offset());
code.erase(4);
code.endInsertion();
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 });
}
}
}
}
}
} // namespace sce::vlt

View file

@ -0,0 +1,362 @@
#pragma once
#include "VltCommon.h"
#include "VltLimit.h"
#include "VltPipeLayout.h"
#include "VltShaderKey.h"
#include "SpirV/SpirvCodeBuffer.h"
#include "SpirV/SpirvCompression.h"
namespace sce::vlt
{
class VltDevice;
class VltShader;
class VltShaderModule;
/**
* \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
{
/// Special constant ranges that do not count
/// towards the spec constant min/max values
ColorComponentMappings = MaxNumResourceSlots,
// Specialization constants for pipeline state
SpecConstantRangeStart = ColorComponentMappings + MaxNumRenderTargets * 4,
RasterizerSampleCount = SpecConstantRangeStart + 0,
FirstPipelineConstant
};
/**
* \brief Shader flags
*
* Provides extra information about the features
* used by a shader.
*/
enum DxvkShaderFlag : uint64_t
{
HasSampleRateShading,
HasTransformFeedback,
ExportsStencilRef,
ExportsViewportIndexLayerFromVertexStage,
};
using VltShaderFlags = util::Flags<DxvkShaderFlag>;
/**
* \brief Shader interface slots
*
* Stores a bit mask of which shader
* interface slots are defined. Used
* purely for validation purposes.
*/
struct VltInterfaceSlots
{
uint32_t inputSlots = 0;
uint32_t outputSlots = 0;
uint32_t pushConstOffset = 0;
uint32_t pushConstSize = 0;
};
/**
* \brief Additional shader options
*
* Contains additional properties that should be
* taken into account when creating pipelines.
*/
struct VltShaderOptions
{
/// Rasterized stream, or -1
int32_t rasterizedStream;
/// Xfb vertex strides
uint32_t xfbStrides[MaxNumXfbBuffers];
};
/**
* \brief Shader constants
*
* Each shader can have constant data associated
* with it, which needs to be copied to a uniform
* buffer. The client API must then bind that buffer
* to an API-specific buffer binding when using the
* shader for rendering.
*/
class VltShaderConstData
{
public:
VltShaderConstData();
VltShaderConstData(
size_t dwordCount,
const uint32_t* dwordArray);
VltShaderConstData(VltShaderConstData&& other);
VltShaderConstData& operator=(VltShaderConstData&& other);
~VltShaderConstData();
const uint32_t* data() const
{
return m_data;
}
size_t sizeInBytes() const
{
return m_size * sizeof(uint32_t);
}
private:
size_t m_size = 0;
uint32_t* m_data = nullptr;
};
/**
* \brief Shader module create info
*/
struct DxvkShaderModuleCreateInfo
{
bool fsDualSrcBlend = false;
uint32_t undefinedInputs = 0;
};
/**
* \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 VltShader : public RcObject
{
public:
VltShader(
VkShaderStageFlagBits stage,
uint32_t slotCount,
const VltResourceSlot* slotInfos,
const VltInterfaceSlots& iface,
pssl::SpirvCodeBuffer code,
const VltShaderOptions& options,
VltShaderConstData&& constData);
~VltShader();
/**
* \brief Shader stage
* \returns Shader stage
*/
VkShaderStageFlagBits stage() const
{
return m_stage;
}
/**
* \brief Retrieves shader flags
* \returns Shader flags
*/
VltShaderFlags flags() const
{
return m_flags;
}
/**
* \brief Adds resource slots definitions to a mapping
*
* Used to generate the exact descriptor set layout when
* compiling a graphics or compute pipeline. Slot indices
* have to be mapped to actual binding numbers.
*/
void defineResourceSlots(
VltDescriptorSlotMapping& mapping) const;
/**
* \brief Creates a shader module
*
* Maps the binding slot numbers
* \param [in] vkd Vulkan device functions
* \param [in] mapping Resource slot mapping
* \param [in] info Module create info
* \returns The shader module
*/
VltShaderModule createShaderModule(
VltDevice* device,
const VltDescriptorSlotMapping& mapping,
const DxvkShaderModuleCreateInfo& info);
/**
* \brief Inter-stage interface slots
*
* Retrieves the input and output
* registers used by the shader.
* \returns Shader interface slots
*/
VltInterfaceSlots interfaceSlots() const
{
return m_interface;
}
/**
* \brief Shader options
* \returns Shader options
*/
VltShaderOptions shaderOptions() const
{
return m_options;
}
/**
* \brief Shader constant data
*
* Returns a read-only reference to the
* constant data associated with this
* shader object.
* \returns Shader constant data
*/
const VltShaderConstData& shaderConstants() const
{
return m_constData;
}
/**
* \brief Dumps SPIR-V shader
*
* Can be used to store the SPIR-V code in a file.
* \param [in] outputStream Stream to write to
*/
void dump(std::ostream& outputStream) const;
/**
* \brief Sets the shader key
* \param [in] key Unique key
*/
void setShaderKey(const VltShaderKey& key)
{
m_key = key;
m_hash = key.hash();
}
/**
* \brief Retrieves shader key
* \returns The unique shader key
*/
VltShaderKey getShaderKey() const
{
return m_key;
}
/**
* \brief Get lookup hash
*
* Retrieves a non-unique hash value derived from the
* shader key which can be used to perform lookups.
* This is better than relying on the pointer value.
* \returns Hash value for map lookups
*/
size_t getHash() const
{
return m_hash;
}
/**
* \brief Retrieves debug name
* \returns The shader's name
*/
std::string debugName() const
{
return m_key.toString();
}
/**
* \brief Get lookup hash for a shader
*
* Convenience method that returns \c 0 for a null
* pointer, and the shader's lookup hash otherwise.
* \param [in] shader The shader
* \returns The shader's lookup hash, or 0
*/
static size_t getHash(const Rc<VltShader>& shader)
{
return shader != nullptr ? shader->getHash() : 0;
}
private:
VkShaderStageFlagBits m_stage;
pssl::SpirvCompressedBuffer m_code;
std::vector<VltResourceSlot> m_slots;
std::vector<size_t> m_idOffsets;
VltInterfaceSlots m_interface;
VltShaderFlags m_flags;
VltShaderOptions m_options;
VltShaderConstData m_constData;
VltShaderKey m_key;
size_t m_hash = 0;
size_t m_o1IdxOffset = 0;
size_t m_o1LocOffset = 0;
static void eliminateInput(pssl::SpirvCodeBuffer& code, uint32_t location);
};
/**
* \brief Shader module object
*
* Manages a Vulkan shader module. This will not
* perform any shader compilation. Instead, the
* context will create pipeline objects on the
* fly when executing draw calls.
*/
class VltShaderModule
{
public:
VltShaderModule();
VltShaderModule(VltShaderModule&& other);
VltShaderModule(
VltDevice* device,
const Rc<VltShader>& shader,
const pssl::SpirvCodeBuffer& code);
~VltShaderModule();
VltShaderModule& operator=(VltShaderModule&& other);
/**
* \brief Shader stage creation info
*
* \param [in] specInfo Specialization info
* \returns Shader stage create info
*/
VkPipelineShaderStageCreateInfo stageInfo(
const VkSpecializationInfo* specInfo) const
{
VkPipelineShaderStageCreateInfo stage = m_stage;
stage.pSpecializationInfo = specInfo;
return stage;
}
/**
* \brief Checks whether module is valid
* \returns \c true if module is valid
*/
operator bool() const
{
return m_stage.module != VK_NULL_HANDLE;
}
private:
VltDevice* m_device;
VkPipelineShaderStageCreateInfo m_stage;
};
} // namespace sce::vlt

View file

@ -0,0 +1,57 @@
#include "VltShaderKey.h"
namespace sce::vlt
{
VltShaderKey::VltShaderKey() :
m_type(0),
m_sha1(alg::Sha1Hash::compute(nullptr, 0))
{
}
std::string VltShaderKey::toString() const
{
const char* prefix = nullptr;
switch (m_type)
{
case VK_SHADER_STAGE_VERTEX_BIT:
prefix = "VS_";
break;
case VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT:
prefix = "TCS_";
break;
case VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT:
prefix = "TES_";
break;
case VK_SHADER_STAGE_GEOMETRY_BIT:
prefix = "GS_";
break;
case VK_SHADER_STAGE_FRAGMENT_BIT:
prefix = "FS_";
break;
case VK_SHADER_STAGE_COMPUTE_BIT:
prefix = "CS_";
break;
default:
prefix = "";
}
return util::str::formatex(prefix, m_sha1.toString());
}
size_t VltShaderKey::hash() const
{
VltHashState result;
result.add(uint32_t(m_type));
for (uint32_t i = 0; i < 5; i++)
result.add(m_sha1.dword(i));
return result;
}
bool VltShaderKey::eq(const VltShaderKey& key) const
{
return m_type == key.m_type && m_sha1 == key.m_sha1;
}
} // namespace sce::vlt

View file

@ -0,0 +1,66 @@
#pragma once
#include "Sha1Hash.h"
#include "VltCommon.h"
#include "VltHash.h"
namespace sce::vlt
{
/**
* \brief Shader key
*
* Provides a unique key that can be used
* to look up a specific shader within a
* structure. This consists of the shader
* stage and the source hash, which should
* be generated from the original code.
*/
class VltShaderKey
{
public:
/**
* \brief Creates default shader key
*/
VltShaderKey();
/**
* \brief Creates shader key
*
* \param [in] stage Shader stage
* \param [in] hash Shader hash
*/
VltShaderKey(
VkShaderStageFlagBits stage,
alg::Sha1Hash hash) :
m_type(stage),
m_sha1(hash)
{
}
/**
* \brief Generates string from shader key
* \returns String representation of the key
*/
std::string toString() const;
/**
* \brief Computes lookup hash
* \returns Lookup hash
*/
size_t hash() const;
/**
* \brief Checks whether two keys are equal
*
* \param [in] key The shader key to compare to
* \returns \c true if the two keys are equal
*/
bool eq(const VltShaderKey& key) const;
private:
VkShaderStageFlags m_type;
alg::Sha1Hash m_sha1;
};
} // namespace sce::vlt

View file

@ -0,0 +1,789 @@
#pragma once
#include "UtilBit.h"
#include "VltBindMask.h"
#include "VltCommon.h"
#include "VltUtil.h"
namespace sce::vlt
{
/**
* \brief Packed input assembly state
*
* Stores the primitive topology
* and primitive restart info.
*/
class VltIaInfo
{
public:
VltIaInfo() = default;
VltIaInfo(
VkPrimitiveTopology primitiveTopology,
VkBool32 primitiveRestart,
uint32_t patchVertexCount) :
m_primitiveTopology(uint16_t(primitiveTopology)),
m_primitiveRestart(uint16_t(primitiveRestart)),
m_patchVertexCount(uint16_t(patchVertexCount)),
m_reserved(0)
{
}
VkPrimitiveTopology primitiveTopology() const
{
return m_primitiveTopology <= VK_PRIMITIVE_TOPOLOGY_PATCH_LIST
? VkPrimitiveTopology(m_primitiveTopology)
: VK_PRIMITIVE_TOPOLOGY_MAX_ENUM;
}
VkBool32 primitiveRestart() const
{
return VkBool32(m_primitiveRestart);
}
uint32_t patchVertexCount() const
{
return m_patchVertexCount;
}
private:
uint16_t m_primitiveTopology : 4;
uint16_t m_primitiveRestart : 1;
uint16_t m_patchVertexCount : 6;
uint16_t m_reserved : 5;
};
/**
* \brief Packed input layout metadata
*
* Stores the number of vertex attributes
* and bindings in one byte each.
*/
class VltIlInfo
{
public:
VltIlInfo() = default;
VltIlInfo(
uint32_t attributeCount,
uint32_t bindingCount) :
m_attributeCount(uint8_t(attributeCount)),
m_bindingCount(uint8_t(bindingCount))
{
}
uint32_t attributeCount() const
{
return m_attributeCount;
}
uint32_t bindingCount() const
{
return m_bindingCount;
}
private:
uint8_t m_attributeCount;
uint8_t m_bindingCount;
};
/**
* \brief Packed vertex attribute
*
* Stores a vertex attribute description. Assumes
* that all vertex formats have numerical values
* of 127 or less (i.e. fit into 7 bits).
*/
class VltIlAttribute
{
public:
VltIlAttribute() = default;
VltIlAttribute(
uint32_t location,
uint32_t binding,
VkFormat format,
uint32_t offset) :
m_location(uint32_t(location)),
m_binding(uint32_t(binding)),
m_format(uint32_t(format)),
m_offset(uint32_t(offset)),
m_reserved(0)
{
}
uint32_t location() const
{
return m_location;
}
uint32_t binding() const
{
return m_binding;
}
VkFormat format() const
{
return VkFormat(m_format);
}
uint32_t offset() const
{
return m_offset;
}
VkVertexInputAttributeDescription description() const
{
VkVertexInputAttributeDescription result;
result.location = m_location;
result.binding = m_binding;
result.format = VkFormat(m_format);
result.offset = m_offset;
return result;
}
private:
uint32_t m_location : 5;
uint32_t m_binding : 5;
uint32_t m_format : 7;
uint32_t m_offset : 11;
uint32_t m_reserved : 4;
};
/**
* \brief Packed vertex binding
*
* Stores a vertex binding description,
* including the 32-bit divisor.
*/
class VltIlBinding
{
public:
VltIlBinding() = default;
VltIlBinding(
uint32_t binding,
uint32_t stride,
VkVertexInputRate inputRate,
uint32_t divisor) :
m_binding(uint32_t(binding)),
m_stride(uint32_t(stride)),
m_inputRate(uint32_t(inputRate)),
m_reserved(0),
m_divisor(divisor)
{
}
uint32_t binding() const
{
return m_binding;
}
uint32_t stride() const
{
return m_stride;
}
VkVertexInputRate inputRate() const
{
return VkVertexInputRate(m_inputRate);
}
uint32_t divisor() const
{
return m_divisor;
}
VkVertexInputBindingDescription description() const
{
VkVertexInputBindingDescription result;
result.binding = m_binding;
result.stride = m_stride;
result.inputRate = VkVertexInputRate(m_inputRate);
return result;
}
void setStride(uint32_t stride)
{
m_stride = stride;
}
private:
uint32_t m_binding : 5;
uint32_t m_stride : 12;
uint32_t m_inputRate : 1;
uint32_t m_reserved : 14;
uint32_t m_divisor;
};
/**
* \brief Packed rasterizer state
*
* Stores a bunch of flags and parameters
* related to rasterization in four bytes.
*/
class VltRsInfo
{
public:
VltRsInfo() = default;
VltRsInfo(
VkBool32 depthClipEnable,
VkBool32 depthBiasEnable,
VkPolygonMode polygonMode,
VkCullModeFlags cullMode,
VkFrontFace frontFace,
uint32_t viewportCount,
VkSampleCountFlags sampleCount) :
m_depthClipEnable(uint32_t(depthClipEnable)),
m_depthBiasEnable(uint32_t(depthBiasEnable)),
m_polygonMode(uint32_t(polygonMode)),
m_cullMode(uint32_t(cullMode)),
m_frontFace(uint32_t(frontFace)),
m_viewportCount(uint32_t(viewportCount)),
m_sampleCount(uint32_t(sampleCount)),
m_reserved(0)
{
}
VkBool32 depthClipEnable() const
{
return VkBool32(m_depthClipEnable);
}
VkBool32 depthBiasEnable() const
{
return VkBool32(m_depthBiasEnable);
}
VkPolygonMode polygonMode() const
{
return VkPolygonMode(m_polygonMode);
}
VkCullModeFlags cullMode() const
{
return VkCullModeFlags(m_cullMode);
}
VkFrontFace frontFace() const
{
return VkFrontFace(m_frontFace);
}
uint32_t viewportCount() const
{
return m_viewportCount;
}
VkSampleCountFlags sampleCount() const
{
return VkSampleCountFlags(m_sampleCount);
}
void setViewportCount(uint32_t viewportCount)
{
m_viewportCount = viewportCount;
}
private:
uint32_t m_depthClipEnable : 1;
uint32_t m_depthBiasEnable : 1;
uint32_t m_polygonMode : 2;
uint32_t m_cullMode : 2;
uint32_t m_frontFace : 1;
uint32_t m_viewportCount : 5;
uint32_t m_sampleCount : 5;
uint32_t m_reserved : 15;
};
/**
* \brief Packed multisample info
*
* Stores the sample mask, sample count override
* and alpha-to-coverage state in four bytes.
*/
class VltMsInfo
{
public:
VltMsInfo() = default;
VltMsInfo(
VkSampleCountFlags sampleCount,
uint32_t sampleMask,
VkBool32 enableAlphaToCoverage) :
m_sampleCount(uint16_t(sampleCount)),
m_enableAlphaToCoverage(uint16_t(enableAlphaToCoverage)),
m_reserved(0),
m_sampleMask(uint16_t(sampleMask))
{
}
VkSampleCountFlags sampleCount() const
{
return VkSampleCountFlags(m_sampleCount);
}
uint32_t sampleMask() const
{
return m_sampleMask;
}
VkBool32 enableAlphaToCoverage() const
{
return VkBool32(m_enableAlphaToCoverage);
}
void setSampleCount(VkSampleCountFlags sampleCount)
{
m_sampleCount = uint16_t(sampleCount);
}
private:
uint16_t m_sampleCount : 5;
uint16_t m_enableAlphaToCoverage : 1;
uint16_t m_reserved : 10;
uint16_t m_sampleMask;
};
/**
* \brief Packed depth-stencil metadata
*
* Stores some flags and the depth-compare op in
* two bytes. Stencil ops are stored separately.
*/
class VltDsInfo
{
public:
VltDsInfo() = default;
VltDsInfo(
VkBool32 enableDepthTest,
VkBool32 enableDepthWrite,
VkBool32 enableDepthBoundsTest,
VkBool32 enableStencilTest,
VkCompareOp depthCompareOp) :
m_enableDepthTest(uint16_t(enableDepthTest)),
m_enableDepthWrite(uint16_t(enableDepthWrite)),
m_enableDepthBoundsTest(uint16_t(enableDepthBoundsTest)),
m_enableStencilTest(uint16_t(enableStencilTest)),
m_depthCompareOp(uint16_t(depthCompareOp)),
m_reserved(0)
{
}
VkBool32 enableDepthTest() const
{
return VkBool32(m_enableDepthTest);
}
VkBool32 enableDepthWrite() const
{
return VkBool32(m_enableDepthWrite);
}
VkBool32 enableDepthBoundsTest() const
{
return VkBool32(m_enableDepthBoundsTest);
}
VkBool32 enableStencilTest() const
{
return VkBool32(m_enableStencilTest);
}
VkCompareOp depthCompareOp() const
{
return VkCompareOp(m_depthCompareOp);
}
void setEnableDepthBoundsTest(VkBool32 enableDepthBoundsTest)
{
m_enableDepthBoundsTest = VkBool32(enableDepthBoundsTest);
}
private:
uint16_t m_enableDepthTest : 1;
uint16_t m_enableDepthWrite : 1;
uint16_t m_enableDepthBoundsTest : 1;
uint16_t m_enableStencilTest : 1;
uint16_t m_depthCompareOp : 3;
uint16_t m_reserved : 9;
};
/**
* \brief Packed stencil op
*
* Stores various stencil op parameters
* for one single face in four bytes.
*/
class VltDsStencilOp
{
public:
VltDsStencilOp() = default;
VltDsStencilOp(VkStencilOpState state) :
m_failOp(uint32_t(state.failOp)),
m_passOp(uint32_t(state.passOp)),
m_depthFailOp(uint32_t(state.depthFailOp)),
m_compareOp(uint32_t(state.compareOp)),
m_reserved(0),
m_compareMask(uint32_t(state.compareMask)),
m_writeMask(uint32_t(state.writeMask))
{
}
VkStencilOpState state() const
{
VkStencilOpState result;
result.failOp = VkStencilOp(m_failOp);
result.passOp = VkStencilOp(m_passOp);
result.depthFailOp = VkStencilOp(m_depthFailOp);
result.compareOp = VkCompareOp(m_compareOp);
result.compareMask = m_compareMask;
result.writeMask = m_writeMask;
result.reference = 0;
return result;
}
private:
uint32_t m_failOp : 3;
uint32_t m_passOp : 3;
uint32_t m_depthFailOp : 3;
uint32_t m_compareOp : 3;
uint32_t m_reserved : 4;
uint32_t m_compareMask : 8;
uint32_t m_writeMask : 8;
};
/**
* \brief Packed color blend metadata
*
* Stores the logic op state in two bytes.
* Blend modes are stored separately.
*/
class VltCbInfo
{
public:
VltCbInfo() = default;
VltCbInfo(
VkBool32 enableLogicOp,
VkLogicOp logicOp) :
m_enableLogicOp(uint16_t(enableLogicOp)),
m_logicOp(uint16_t(logicOp)),
m_reserved(0)
{
}
VkBool32 enableLogicOp() const
{
return VkBool32(m_enableLogicOp);
}
VkLogicOp logicOp() const
{
return VkLogicOp(m_logicOp);
}
private:
uint16_t m_enableLogicOp : 1;
uint16_t m_logicOp : 4;
uint16_t m_reserved : 11;
};
/**
* \brief Packed attachment blend mode
*
* Stores blendig parameters for a single
* color attachment in four bytes.
*/
class VltCbAttachmentBlend
{
public:
VltCbAttachmentBlend() = default;
VltCbAttachmentBlend(
VkBool32 blendEnable,
VkBlendFactor srcColorBlendFactor,
VkBlendFactor dstColorBlendFactor,
VkBlendOp colorBlendOp,
VkBlendFactor srcAlphaBlendFactor,
VkBlendFactor dstAlphaBlendFactor,
VkBlendOp alphaBlendOp,
VkColorComponentFlags colorWriteMask) :
m_blendEnable(uint32_t(blendEnable)),
m_srcColorBlendFactor(uint32_t(srcColorBlendFactor)),
m_dstColorBlendFactor(uint32_t(dstColorBlendFactor)),
m_colorBlendOp(uint32_t(colorBlendOp)),
m_srcAlphaBlendFactor(uint32_t(srcAlphaBlendFactor)),
m_dstAlphaBlendFactor(uint32_t(dstAlphaBlendFactor)),
m_alphaBlendOp(uint32_t(alphaBlendOp)),
m_colorWriteMask(uint32_t(colorWriteMask)),
m_reserved(0)
{
}
VkBool32 blendEnable() const
{
return m_blendEnable;
}
VkBlendFactor srcColorBlendFactor() const
{
return VkBlendFactor(m_srcColorBlendFactor);
}
VkBlendFactor dstColorBlendFactor() const
{
return VkBlendFactor(m_dstColorBlendFactor);
}
VkBlendOp colorBlendOp() const
{
return VkBlendOp(m_colorBlendOp);
}
VkBlendFactor srcAlphaBlendFactor() const
{
return VkBlendFactor(m_srcAlphaBlendFactor);
}
VkBlendFactor dstAlphaBlendFactor() const
{
return VkBlendFactor(m_dstAlphaBlendFactor);
}
VkBlendOp alphaBlendOp() const
{
return VkBlendOp(m_alphaBlendOp);
}
VkColorComponentFlags colorWriteMask() const
{
return VkColorComponentFlags(m_colorWriteMask);
}
VkPipelineColorBlendAttachmentState state() const
{
VkPipelineColorBlendAttachmentState result;
result.blendEnable = VkBool32(m_blendEnable);
result.srcColorBlendFactor = VkBlendFactor(m_srcColorBlendFactor);
result.dstColorBlendFactor = VkBlendFactor(m_dstColorBlendFactor);
result.colorBlendOp = VkBlendOp(m_colorBlendOp);
result.srcAlphaBlendFactor = VkBlendFactor(m_srcAlphaBlendFactor);
result.dstAlphaBlendFactor = VkBlendFactor(m_dstAlphaBlendFactor);
result.alphaBlendOp = VkBlendOp(m_alphaBlendOp);
result.colorWriteMask = VkColorComponentFlags(m_colorWriteMask);
return result;
}
private:
uint32_t m_blendEnable : 1;
uint32_t m_srcColorBlendFactor : 5;
uint32_t m_dstColorBlendFactor : 5;
uint32_t m_colorBlendOp : 3;
uint32_t m_srcAlphaBlendFactor : 5;
uint32_t m_dstAlphaBlendFactor : 5;
uint32_t m_alphaBlendOp : 3;
uint32_t m_colorWriteMask : 4;
uint32_t m_reserved : 1;
};
/**
* \brief Packed attachment swizzle
*
* Stores the component mapping for one
* single color attachment in one byte.
*/
class VltCbAttachmentSwizzle
{
public:
VltCbAttachmentSwizzle() = default;
VltCbAttachmentSwizzle(VkComponentMapping mapping) :
m_r(vutil::getComponentIndex(mapping.r, 0)),
m_g(vutil::getComponentIndex(mapping.g, 1)),
m_b(vutil::getComponentIndex(mapping.b, 2)),
m_a(vutil::getComponentIndex(mapping.a, 3))
{
}
uint32_t rIndex() const
{
return m_r;
}
uint32_t gIndex() const
{
return m_g;
}
uint32_t bIndex() const
{
return m_b;
}
uint32_t aIndex() const
{
return m_a;
}
VkComponentMapping mapping() const
{
VkComponentMapping result;
result.r = decodeSwizzle(m_r);
result.g = decodeSwizzle(m_g);
result.b = decodeSwizzle(m_b);
result.a = decodeSwizzle(m_a);
return result;
}
private:
uint8_t m_r : 2;
uint8_t m_g : 2;
uint8_t m_b : 2;
uint8_t m_a : 2;
static VkComponentSwizzle decodeSwizzle(uint8_t swizzle)
{
return VkComponentSwizzle(uint32_t(swizzle) + uint32_t(VK_COMPONENT_SWIZZLE_R));
}
};
/**
* \brief Specialization constant state
*
* Stores the raw 32-bit spec constant values.
*/
struct VltScInfo
{
uint32_t specConstants[VltLimits::MaxNumSpecConstants];
};
/**
* \brief Packed graphics pipeline state
*
* Stores a compressed representation of the full
* graphics pipeline state which is optimized for
* lookup performance.
*/
struct alignas(32) VltGraphicsPipelineStateInfo
{
VltGraphicsPipelineStateInfo()
{
std::memset(this, 0, sizeof(*this));
}
VltGraphicsPipelineStateInfo(const VltGraphicsPipelineStateInfo& other)
{
std::memcpy(this, &other, sizeof(*this));
}
VltGraphicsPipelineStateInfo& operator=(const VltGraphicsPipelineStateInfo& other)
{
std::memcpy(this, &other, sizeof(*this));
return *this;
}
bool operator==(const VltGraphicsPipelineStateInfo& other) const
{
return util::bit::bcmpeq(this, &other);
}
bool operator!=(const VltGraphicsPipelineStateInfo& other) const
{
return !util::bit::bcmpeq(this, &other);
}
bool useDynamicStencilRef() const
{
return ds.enableStencilTest();
}
bool useDynamicDepthBias() const
{
return rs.depthBiasEnable();
}
bool useDynamicDepthBounds() const
{
return ds.enableDepthBoundsTest();
}
bool useDynamicBlendConstants() const
{
bool result = false;
for (uint32_t i = 0; i < MaxNumRenderTargets && !result; i++)
{
result |= cbBlend[i].blendEnable() &&
(vutil::isBlendConstantBlendFactor(cbBlend[i].srcColorBlendFactor()) ||
vutil::isBlendConstantBlendFactor(cbBlend[i].dstColorBlendFactor()) ||
vutil::isBlendConstantBlendFactor(cbBlend[i].srcAlphaBlendFactor()) ||
vutil::isBlendConstantBlendFactor(cbBlend[i].dstAlphaBlendFactor()));
}
return result;
}
VltBindingMask bsBindingMask;
VltIaInfo ia;
VltIlInfo il;
VltRsInfo rs;
VltMsInfo ms;
VltDsInfo ds;
VltCbInfo cb;
VltScInfo sc;
VltDsStencilOp dsFront;
VltDsStencilOp dsBack;
VltCbAttachmentSwizzle cbSwizzle[VltLimits::MaxNumRenderTargets];
VltCbAttachmentBlend cbBlend[VltLimits::MaxNumRenderTargets];
VltIlAttribute ilAttributes[VltLimits::MaxNumVertexAttributes];
VltIlBinding ilBindings[VltLimits::MaxNumVertexBindings];
};
/**
* \brief Compute pipeline state info
*/
struct alignas(32) VltComputePipelineStateInfo
{
VltComputePipelineStateInfo()
{
std::memset(this, 0, sizeof(*this));
}
VltComputePipelineStateInfo(const VltComputePipelineStateInfo& other)
{
std::memcpy(this, &other, sizeof(*this));
}
VltComputePipelineStateInfo& operator=(const VltComputePipelineStateInfo& other)
{
std::memcpy(this, &other, sizeof(*this));
return *this;
}
bool operator==(const VltComputePipelineStateInfo& other) const
{
return util::bit::bcmpeq(this, &other);
}
bool operator!=(const VltComputePipelineStateInfo& other) const
{
return !util::bit::bcmpeq(this, &other);
}
VltBindingMask bsBindingMask;
VltScInfo sc;
};
} // namespace sce::vlt

View file

@ -370,12 +370,18 @@ namespace sce::vlt::vutil
bool isBlendConstantBlendFactor(VkBlendFactor factor)
{
return factor == VK_BLEND_FACTOR_CONSTANT_COLOR || factor == VK_BLEND_FACTOR_CONSTANT_ALPHA || factor == VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR || factor == VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
return factor == VK_BLEND_FACTOR_CONSTANT_COLOR ||
factor == VK_BLEND_FACTOR_CONSTANT_ALPHA ||
factor == VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR ||
factor == VK_BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA;
}
bool isDualSourceBlendFactor(VkBlendFactor factor)
{
return factor == VK_BLEND_FACTOR_SRC1_COLOR || factor == VK_BLEND_FACTOR_SRC1_ALPHA || factor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR || factor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
return factor == VK_BLEND_FACTOR_SRC1_COLOR ||
factor == VK_BLEND_FACTOR_SRC1_ALPHA ||
factor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_COLOR ||
factor == VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA;
}
VltAccessFlags getAccessTypes(VkAccessFlags2 flags)