Initial support for asyncronous shader pipeline compilation

This commit is contained in:
Jack Andersen 2019-06-15 20:24:28 -10:00
parent 29a67b9ea8
commit 7eda81e55e
6 changed files with 342 additions and 231 deletions

View File

@ -46,7 +46,7 @@ public:
ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment, ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo); const AdditionalPipelineInfo& additionalInfo, bool asynchronous = true);
ObjToken<IShaderDataBinding> newShaderDataBinding( ObjToken<IShaderDataBinding> newShaderDataBinding(
const ObjToken<IShaderPipeline>& pipeline, const ObjToken<IGraphicsBuffer>& vbo, const ObjToken<IShaderPipeline>& pipeline, const ObjToken<IGraphicsBuffer>& vbo,

View File

@ -145,7 +145,9 @@ struct IShaderStage : IObj {};
/** Opaque token for referencing a complete graphics pipeline state necessary /** Opaque token for referencing a complete graphics pipeline state necessary
* to rasterize geometry (shaders and blending modes mainly) */ * to rasterize geometry (shaders and blending modes mainly) */
struct IShaderPipeline : IObj {}; struct IShaderPipeline : IObj {
virtual bool isReady() const = 0;
};
/** Opaque token serving as indirection table for shader resources /** Opaque token serving as indirection table for shader resources
* and IShaderPipeline reference. Each renderable surface-material holds one * and IShaderPipeline reference. Each renderable surface-material holds one
@ -241,12 +243,14 @@ struct IGraphicsDataFactory {
ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
ObjToken<IShaderStage> evaluation, ObjToken<IShaderStage> evaluation,
const VertexFormatInfo& vtxFmt, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo) = 0; const AdditionalPipelineInfo& additionalInfo,
bool asynchronous = true) = 0;
ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment, ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
const VertexFormatInfo& vtxFmt, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo) { const AdditionalPipelineInfo& additionalInfo,
return newShaderPipeline(vertex, fragment, {}, {}, {}, vtxFmt, additionalInfo); bool asynchronous = true) {
return newShaderPipeline(vertex, fragment, {}, {}, {}, vtxFmt, additionalInfo, asynchronous);
} }
virtual ObjToken<IShaderDataBinding> newShaderDataBinding( virtual ObjToken<IShaderDataBinding> newShaderDataBinding(
@ -273,6 +277,7 @@ struct IGraphicsDataFactory {
virtual ObjToken<IGraphicsBufferD> newPoolBuffer(BufferUse use, size_t stride, size_t count __BooTraceArgs) = 0; virtual ObjToken<IGraphicsBufferD> newPoolBuffer(BufferUse use, size_t stride, size_t count __BooTraceArgs) = 0;
virtual void setDisplayGamma(float gamma) = 0; virtual void setDisplayGamma(float gamma) = 0;
virtual bool isTessellationSupported(uint32_t& maxPatchSizeOut) = 0; virtual bool isTessellationSupported(uint32_t& maxPatchSizeOut) = 0;
virtual void waitUntilShadersReady() = 0;
}; };
using GraphicsDataFactoryContext = IGraphicsDataFactory::Context; using GraphicsDataFactoryContext = IGraphicsDataFactory::Context;

View File

@ -155,7 +155,7 @@ public:
ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment, ObjToken<IShaderPipeline> newShaderPipeline(ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment,
ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo); const AdditionalPipelineInfo& additionalInfo, bool asynchronous = true);
boo::ObjToken<IShaderDataBinding> newShaderDataBinding( boo::ObjToken<IShaderDataBinding> newShaderDataBinding(
const boo::ObjToken<IShaderPipeline>& pipeline, const boo::ObjToken<IGraphicsBuffer>& vbo, const boo::ObjToken<IShaderPipeline>& pipeline, const boo::ObjToken<IGraphicsBuffer>& vbo,

View File

@ -7,6 +7,10 @@
#include <vector> #include <vector>
#include <mutex> #include <mutex>
#include <cassert> #include <cassert>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include "boo/graphicsdev/IGraphicsDataFactory.hpp" #include "boo/graphicsdev/IGraphicsDataFactory.hpp"
#include "../Common.hpp" #include "../Common.hpp"
@ -196,4 +200,68 @@ struct GraphicsDataNode : ListNode<GraphicsDataNode<NodeCls, DataCls>, ObjToken<
void UpdateGammaLUT(ITextureD* tex, float gamma); void UpdateGammaLUT(ITextureD* tex, float gamma);
/** Generic work-queue for asynchronously building shader pipelines on supported backends
*/
template <class ShaderPipelineType>
class PipelineCompileQueue {
struct Task {
ObjToken<IShaderPipeline> m_pipeline;
explicit Task(ObjToken<IShaderPipeline> pipeline) : m_pipeline(pipeline) {}
void run() {
m_pipeline.cast<ShaderPipelineType>()->compile();
}
};
std::queue<Task> m_tasks;
size_t m_outstandingTasks = 0;
std::vector<std::thread> m_threads;
std::mutex m_mt;
std::condition_variable m_cv, m_backcv;
bool m_running = true;
void worker() {
std::unique_lock<std::mutex> lk(m_mt);
while (m_running) {
m_cv.wait(lk, [this]() { return !m_tasks.empty() || !m_running; });
if (!m_running)
break;
Task t = std::move(m_tasks.front());
m_tasks.pop();
lk.unlock();
t.run();
lk.lock();
--m_outstandingTasks;
m_backcv.notify_all();
}
}
public:
void addPipeline(ObjToken<IShaderPipeline> pipeline) {
std::lock_guard<std::mutex> lk(m_mt);
m_tasks.emplace(pipeline);
++m_outstandingTasks;
m_cv.notify_one();
}
void waitUntilReady() {
std::unique_lock<std::mutex> lk(m_mt);
m_backcv.wait(lk, [this]() { return m_outstandingTasks == 0 || !m_running; });
}
PipelineCompileQueue() {
unsigned int numThreads = std::thread::hardware_concurrency();
if (numThreads > 1)
--numThreads;
m_threads.reserve(numThreads);
for (unsigned int i = 0; i < numThreads; ++i)
m_threads.emplace_back(std::bind(&PipelineCompileQueue::worker, this));
}
~PipelineCompileQueue() {
m_running = false;
m_cv.notify_all();
for (auto& t : m_threads) t.join();
}
};
} // namespace boo } // namespace boo

View File

@ -134,6 +134,8 @@ public:
maxPatchSizeOut = m_maxPatchSize; maxPatchSizeOut = m_maxPatchSize;
return m_hasTessellation; return m_hasTessellation;
} }
void waitUntilShadersReady() {}
}; };
static const GLenum USE_TABLE[] = {GL_INVALID_ENUM, GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_UNIFORM_BUFFER}; static const GLenum USE_TABLE[] = {GL_INVALID_ENUM, GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_UNIFORM_BUFFER};
@ -962,6 +964,8 @@ public:
return m_prog; return m_prog;
} }
bool isReady() const { return true; }
}; };
ObjToken<IShaderStage> GLDataFactory::Context::newShaderStage(const uint8_t* data, size_t size, PipelineStage stage) { ObjToken<IShaderStage> GLDataFactory::Context::newShaderStage(const uint8_t* data, size_t size, PipelineStage stage) {
@ -979,7 +983,7 @@ ObjToken<IShaderStage> GLDataFactory::Context::newShaderStage(const uint8_t* dat
ObjToken<IShaderPipeline> GLDataFactory::Context::newShaderPipeline( ObjToken<IShaderPipeline> GLDataFactory::Context::newShaderPipeline(
ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment, ObjToken<IShaderStage> geometry,
ObjToken<IShaderStage> control, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt, ObjToken<IShaderStage> control, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo) { const AdditionalPipelineInfo& additionalInfo, bool asynchronous) {
GLDataFactoryImpl& factory = static_cast<GLDataFactoryImpl&>(m_parent); GLDataFactoryImpl& factory = static_cast<GLDataFactoryImpl&>(m_parent);
if (control || evaluation) { if (control || evaluation) {

View File

@ -72,10 +72,13 @@ class VulkanDataFactoryImpl : public VulkanDataFactory, public GraphicsDataFacto
friend struct VulkanPool; friend struct VulkanPool;
friend struct VulkanDescriptorPool; friend struct VulkanDescriptorPool;
friend struct VulkanShaderDataBinding; friend struct VulkanShaderDataBinding;
IGraphicsContext* m_parent; IGraphicsContext* m_parent;
VulkanContext* m_ctx; VulkanContext* m_ctx;
VulkanDescriptorPool* m_descPoolHead = nullptr; VulkanDescriptorPool* m_descPoolHead = nullptr;
PipelineCompileQueue<class VulkanShaderPipeline> m_pipelineQueue;
float m_gamma = 1.f; float m_gamma = 1.f;
ObjToken<IShaderPipeline> m_gammaShader; ObjToken<IShaderPipeline> m_gammaShader;
ObjToken<ITextureD> m_gammaLUT; ObjToken<ITextureD> m_gammaLUT;
@ -90,7 +93,7 @@ class VulkanDataFactoryImpl : public VulkanDataFactory, public GraphicsDataFacto
const VertexElementDescriptor vfmt[] = {{VertexSemantic::Position4}, {VertexSemantic::UV4}}; const VertexElementDescriptor vfmt[] = {{VertexSemantic::Position4}, {VertexSemantic::UV4}};
AdditionalPipelineInfo info = { AdditionalPipelineInfo info = {
BlendFactor::One, BlendFactor::Zero, Primitive::TriStrips, ZTest::None, false, true, false, CullMode::None}; BlendFactor::One, BlendFactor::Zero, Primitive::TriStrips, ZTest::None, false, true, false, CullMode::None};
m_gammaShader = ctx.newShaderPipeline(vertexShader, fragmentShader, vfmt, info); m_gammaShader = ctx.newShaderPipeline(vertexShader, fragmentShader, vfmt, info, false);
m_gammaLUT = ctx.newDynamicTexture(256, 256, TextureFormat::I16, TextureClampMode::ClampToEdge); m_gammaLUT = ctx.newDynamicTexture(256, 256, TextureFormat::I16, TextureClampMode::ClampToEdge);
setDisplayGamma(1.f); setDisplayGamma(1.f);
const struct Vert { const struct Vert {
@ -140,6 +143,10 @@ public:
maxPatchSizeOut = m_ctx->m_gpuProps.limits.maxTessellationPatchSize; maxPatchSizeOut = m_ctx->m_gpuProps.limits.maxTessellationPatchSize;
return true; return true;
} }
void waitUntilShadersReady() {
m_pipelineQueue.waitUntilReady();
}
}; };
static inline void ThrowIfFailed(VkResult res) { static inline void ThrowIfFailed(VkResult res) {
@ -489,6 +496,9 @@ void VulkanContext::initDevice() {
tessellationDescriptorBit = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; tessellationDescriptorBit = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
features.tessellationShader = VK_TRUE; features.tessellationShader = VK_TRUE;
} }
if (!m_features.dualSrcBlend)
Log.report(logvisor::Fatal, "Vulkan device does not support dual-source blending");
features.dualSrcBlend = VK_TRUE;
uint32_t extCount = 0; uint32_t extCount = 0;
vk::EnumerateDeviceExtensionProperties(m_gpus[0], nullptr, &extCount, nullptr); vk::EnumerateDeviceExtensionProperties(m_gpus[0], nullptr, &extCount, nullptr);
@ -2258,6 +2268,7 @@ public:
class VulkanShaderPipeline : public GraphicsDataNode<IShaderPipeline> { class VulkanShaderPipeline : public GraphicsDataNode<IShaderPipeline> {
protected: protected:
friend class VulkanDataFactory; friend class VulkanDataFactory;
friend class VulkanDataFactoryImpl;
friend struct VulkanShaderDataBinding; friend struct VulkanShaderDataBinding;
VulkanContext* m_ctx; VulkanContext* m_ctx;
VkPipelineCache m_pipelineCache; VkPipelineCache m_pipelineCache;
@ -2277,12 +2288,13 @@ protected:
bool m_overwriteAlpha; bool m_overwriteAlpha;
CullMode m_culling; CullMode m_culling;
uint32_t m_patchSize; uint32_t m_patchSize;
mutable VkPipeline m_pipeline = VK_NULL_HANDLE; bool m_asynchronous;
mutable std::atomic<VkPipeline> m_pipeline = VK_NULL_HANDLE;
VulkanShaderPipeline(const boo::ObjToken<BaseGraphicsData>& parent, VulkanContext* ctx, ObjToken<IShaderStage> vertex, VulkanShaderPipeline(const boo::ObjToken<BaseGraphicsData>& parent, VulkanContext* ctx, ObjToken<IShaderStage> vertex,
ObjToken<IShaderStage> fragment, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control, ObjToken<IShaderStage> fragment, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> control,
ObjToken<IShaderStage> evaluation, VkPipelineCache pipelineCache, const VertexFormatInfo& vtxFmt, ObjToken<IShaderStage> evaluation, VkPipelineCache pipelineCache, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& info) const AdditionalPipelineInfo& info, bool asynchronous)
: GraphicsDataNode<IShaderPipeline>(parent) : GraphicsDataNode<IShaderPipeline>(parent)
, m_ctx(ctx) , m_ctx(ctx)
, m_pipelineCache(pipelineCache) , m_pipelineCache(pipelineCache)
@ -2301,7 +2313,8 @@ protected:
, m_alphaWrite(info.alphaWrite) , m_alphaWrite(info.alphaWrite)
, m_overwriteAlpha(info.overwriteAlpha) , m_overwriteAlpha(info.overwriteAlpha)
, m_culling(info.culling) , m_culling(info.culling)
, m_patchSize(info.patchSize) { , m_patchSize(info.patchSize)
, m_asynchronous(asynchronous) {
if (control && evaluation) if (control && evaluation)
m_prim = Primitive::Patches; m_prim = Primitive::Patches;
} }
@ -2316,226 +2329,239 @@ public:
VulkanShaderPipeline& operator=(const VulkanShaderPipeline&) = delete; VulkanShaderPipeline& operator=(const VulkanShaderPipeline&) = delete;
VulkanShaderPipeline(const VulkanShaderPipeline&) = delete; VulkanShaderPipeline(const VulkanShaderPipeline&) = delete;
VkPipeline bind(VkRenderPass rPass = 0) const { VkPipeline bind(VkRenderPass rPass = 0) const {
if (!m_pipeline) { compile(rPass);
if (!rPass) while (m_pipeline == VK_NULL_HANDLE) {}
rPass = m_ctx->m_pass;
VkCullModeFlagBits cullMode;
switch (m_culling) {
case CullMode::None:
default:
cullMode = VK_CULL_MODE_NONE;
break;
case CullMode::Backface:
cullMode = VK_CULL_MODE_BACK_BIT;
break;
case CullMode::Frontface:
cullMode = VK_CULL_MODE_FRONT_BIT;
break;
}
VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE] = {};
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.pNext = nullptr;
dynamicState.pDynamicStates = dynamicStateEnables;
dynamicState.dynamicStateCount = 0;
VkPipelineShaderStageCreateInfo stages[5] = {};
uint32_t numStages = 0;
if (m_vertex) {
stages[numStages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[numStages].pNext = nullptr;
stages[numStages].flags = 0;
stages[numStages].stage = VK_SHADER_STAGE_VERTEX_BIT;
stages[numStages].module = m_vertex.cast<VulkanShaderStage>()->shader();
stages[numStages].pName = "main";
stages[numStages++].pSpecializationInfo = nullptr;
}
if (m_fragment) {
stages[numStages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[numStages].pNext = nullptr;
stages[numStages].flags = 0;
stages[numStages].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
stages[numStages].module = m_fragment.cast<VulkanShaderStage>()->shader();
stages[numStages].pName = "main";
stages[numStages++].pSpecializationInfo = nullptr;
}
if (m_geometry) {
stages[numStages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[numStages].pNext = nullptr;
stages[numStages].flags = 0;
stages[numStages].stage = VK_SHADER_STAGE_GEOMETRY_BIT;
stages[numStages].module = m_geometry.cast<VulkanShaderStage>()->shader();
stages[numStages].pName = "main";
stages[numStages++].pSpecializationInfo = nullptr;
}
if (m_control) {
stages[numStages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[numStages].pNext = nullptr;
stages[numStages].flags = 0;
stages[numStages].stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
stages[numStages].module = m_control.cast<VulkanShaderStage>()->shader();
stages[numStages].pName = "main";
stages[numStages++].pSpecializationInfo = nullptr;
}
if (m_evaluation) {
stages[numStages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[numStages].pNext = nullptr;
stages[numStages].flags = 0;
stages[numStages].stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
stages[numStages].module = m_evaluation.cast<VulkanShaderStage>()->shader();
stages[numStages].pName = "main";
stages[numStages++].pSpecializationInfo = nullptr;
}
VkPipelineInputAssemblyStateCreateInfo assemblyInfo = {};
assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
assemblyInfo.pNext = nullptr;
assemblyInfo.flags = 0;
assemblyInfo.topology = PRIMITIVE_TABLE[int(m_prim)];
assemblyInfo.primitiveRestartEnable = VK_TRUE;
VkPipelineTessellationStateCreateInfo tessInfo = {};
tessInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
tessInfo.pNext = nullptr;
tessInfo.flags = 0;
tessInfo.patchControlPoints = m_patchSize;
VkPipelineViewportStateCreateInfo viewportInfo = {};
viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportInfo.pNext = nullptr;
viewportInfo.flags = 0;
viewportInfo.viewportCount = 1;
viewportInfo.pViewports = nullptr;
viewportInfo.scissorCount = 1;
viewportInfo.pScissors = nullptr;
dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
#if AMD_PAL_HACK
dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;
#endif
VkPipelineRasterizationStateCreateInfo rasterizationInfo = {};
rasterizationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizationInfo.pNext = nullptr;
rasterizationInfo.flags = 0;
rasterizationInfo.depthClampEnable = VK_FALSE;
rasterizationInfo.rasterizerDiscardEnable = VK_FALSE;
rasterizationInfo.polygonMode = VK_POLYGON_MODE_FILL;
rasterizationInfo.cullMode = cullMode;
rasterizationInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizationInfo.depthBiasEnable = VK_FALSE;
rasterizationInfo.lineWidth = 1.f;
VkPipelineMultisampleStateCreateInfo multisampleInfo = {};
multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleInfo.pNext = nullptr;
multisampleInfo.flags = 0;
multisampleInfo.rasterizationSamples = VkSampleCountFlagBits(m_ctx->m_sampleCountColor);
VkPipelineDepthStencilStateCreateInfo depthStencilInfo = {};
depthStencilInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencilInfo.pNext = nullptr;
depthStencilInfo.flags = 0;
depthStencilInfo.depthTestEnable = m_depthTest != ZTest::None;
depthStencilInfo.depthWriteEnable = m_depthWrite;
depthStencilInfo.front.compareOp = VK_COMPARE_OP_ALWAYS;
depthStencilInfo.back.compareOp = VK_COMPARE_OP_ALWAYS;
switch (m_depthTest) {
case ZTest::None:
default:
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_ALWAYS;
break;
case ZTest::LEqual:
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
break;
case ZTest::Greater:
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_GREATER;
break;
case ZTest::Equal:
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_EQUAL;
break;
case ZTest::GEqual:
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL;
break;
}
VkPipelineColorBlendAttachmentState colorAttachment = {};
colorAttachment.blendEnable = m_dstFac != BlendFactor::Zero;
if (m_srcFac == BlendFactor::Subtract || m_dstFac == BlendFactor::Subtract) {
colorAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorAttachment.colorBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT;
if (m_overwriteAlpha) {
colorAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
} else {
colorAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorAttachment.alphaBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT;
}
} else {
colorAttachment.srcColorBlendFactor = BLEND_FACTOR_TABLE[int(m_srcFac)];
colorAttachment.dstColorBlendFactor = BLEND_FACTOR_TABLE[int(m_dstFac)];
colorAttachment.colorBlendOp = VK_BLEND_OP_ADD;
if (m_overwriteAlpha) {
colorAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
} else {
colorAttachment.srcAlphaBlendFactor = BLEND_FACTOR_TABLE[int(m_srcFac)];
colorAttachment.dstAlphaBlendFactor = BLEND_FACTOR_TABLE[int(m_dstFac)];
}
colorAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
}
colorAttachment.colorWriteMask =
(m_colorWrite ? (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT) : 0) |
(m_alphaWrite ? VK_COLOR_COMPONENT_A_BIT : 0);
VkPipelineColorBlendStateCreateInfo colorBlendInfo = {};
colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendInfo.pNext = nullptr;
colorBlendInfo.flags = 0;
colorBlendInfo.logicOpEnable = VK_FALSE;
colorBlendInfo.attachmentCount = 1;
colorBlendInfo.pAttachments = &colorAttachment;
VkGraphicsPipelineCreateInfo pipelineCreateInfo = {};
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCreateInfo.pNext = nullptr;
pipelineCreateInfo.flags = 0;
pipelineCreateInfo.stageCount = numStages;
pipelineCreateInfo.pStages = stages;
pipelineCreateInfo.pVertexInputState = &m_vtxFmt.m_info;
pipelineCreateInfo.pInputAssemblyState = &assemblyInfo;
pipelineCreateInfo.pTessellationState = &tessInfo;
pipelineCreateInfo.pViewportState = &viewportInfo;
pipelineCreateInfo.pRasterizationState = &rasterizationInfo;
pipelineCreateInfo.pMultisampleState = &multisampleInfo;
pipelineCreateInfo.pDepthStencilState = &depthStencilInfo;
pipelineCreateInfo.pColorBlendState = &colorBlendInfo;
pipelineCreateInfo.pDynamicState = &dynamicState;
pipelineCreateInfo.layout = m_ctx->m_pipelinelayout;
pipelineCreateInfo.renderPass = rPass;
ThrowIfFailed(
vk::CreateGraphicsPipelines(m_ctx->m_dev, m_pipelineCache, 1, &pipelineCreateInfo, nullptr, &m_pipeline));
m_vertex.reset();
m_fragment.reset();
m_geometry.reset();
m_control.reset();
m_evaluation.reset();
}
return m_pipeline; return m_pipeline;
} }
mutable std::atomic_bool m_startCompile = {};
void compile(VkRenderPass rPass = 0) const {
bool falseCmp = false;
if (m_startCompile.compare_exchange_strong(falseCmp, true)) {
if (!m_pipeline) {
if (!rPass)
rPass = m_ctx->m_pass;
VkCullModeFlagBits cullMode;
switch (m_culling) {
case CullMode::None:
default:
cullMode = VK_CULL_MODE_NONE;
break;
case CullMode::Backface:
cullMode = VK_CULL_MODE_BACK_BIT;
break;
case CullMode::Frontface:
cullMode = VK_CULL_MODE_FRONT_BIT;
break;
}
VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE] = {};
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.pNext = nullptr;
dynamicState.pDynamicStates = dynamicStateEnables;
dynamicState.dynamicStateCount = 0;
VkPipelineShaderStageCreateInfo stages[5] = {};
uint32_t numStages = 0;
if (m_vertex) {
stages[numStages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[numStages].pNext = nullptr;
stages[numStages].flags = 0;
stages[numStages].stage = VK_SHADER_STAGE_VERTEX_BIT;
stages[numStages].module = m_vertex.cast<VulkanShaderStage>()->shader();
stages[numStages].pName = "main";
stages[numStages++].pSpecializationInfo = nullptr;
}
if (m_fragment) {
stages[numStages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[numStages].pNext = nullptr;
stages[numStages].flags = 0;
stages[numStages].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
stages[numStages].module = m_fragment.cast<VulkanShaderStage>()->shader();
stages[numStages].pName = "main";
stages[numStages++].pSpecializationInfo = nullptr;
}
if (m_geometry) {
stages[numStages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[numStages].pNext = nullptr;
stages[numStages].flags = 0;
stages[numStages].stage = VK_SHADER_STAGE_GEOMETRY_BIT;
stages[numStages].module = m_geometry.cast<VulkanShaderStage>()->shader();
stages[numStages].pName = "main";
stages[numStages++].pSpecializationInfo = nullptr;
}
if (m_control) {
stages[numStages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[numStages].pNext = nullptr;
stages[numStages].flags = 0;
stages[numStages].stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
stages[numStages].module = m_control.cast<VulkanShaderStage>()->shader();
stages[numStages].pName = "main";
stages[numStages++].pSpecializationInfo = nullptr;
}
if (m_evaluation) {
stages[numStages].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[numStages].pNext = nullptr;
stages[numStages].flags = 0;
stages[numStages].stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
stages[numStages].module = m_evaluation.cast<VulkanShaderStage>()->shader();
stages[numStages].pName = "main";
stages[numStages++].pSpecializationInfo = nullptr;
}
VkPipelineInputAssemblyStateCreateInfo assemblyInfo = {};
assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
assemblyInfo.pNext = nullptr;
assemblyInfo.flags = 0;
assemblyInfo.topology = PRIMITIVE_TABLE[int(m_prim)];
assemblyInfo.primitiveRestartEnable = m_prim == Primitive::TriStrips;
VkPipelineTessellationStateCreateInfo tessInfo = {};
tessInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
tessInfo.pNext = nullptr;
tessInfo.flags = 0;
tessInfo.patchControlPoints = m_patchSize;
VkPipelineViewportStateCreateInfo viewportInfo = {};
viewportInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportInfo.pNext = nullptr;
viewportInfo.flags = 0;
viewportInfo.viewportCount = 1;
viewportInfo.pViewports = nullptr;
viewportInfo.scissorCount = 1;
viewportInfo.pScissors = nullptr;
dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
#if AMD_PAL_HACK
dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_BLEND_CONSTANTS;
#endif
VkPipelineRasterizationStateCreateInfo rasterizationInfo = {};
rasterizationInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizationInfo.pNext = nullptr;
rasterizationInfo.flags = 0;
rasterizationInfo.depthClampEnable = VK_FALSE;
rasterizationInfo.rasterizerDiscardEnable = VK_FALSE;
rasterizationInfo.polygonMode = VK_POLYGON_MODE_FILL;
rasterizationInfo.cullMode = cullMode;
rasterizationInfo.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizationInfo.depthBiasEnable = VK_FALSE;
rasterizationInfo.lineWidth = 1.f;
VkPipelineMultisampleStateCreateInfo multisampleInfo = {};
multisampleInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleInfo.pNext = nullptr;
multisampleInfo.flags = 0;
multisampleInfo.rasterizationSamples = VkSampleCountFlagBits(m_ctx->m_sampleCountColor);
VkPipelineDepthStencilStateCreateInfo depthStencilInfo = {};
depthStencilInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencilInfo.pNext = nullptr;
depthStencilInfo.flags = 0;
depthStencilInfo.depthTestEnable = m_depthTest != ZTest::None;
depthStencilInfo.depthWriteEnable = m_depthWrite;
depthStencilInfo.front.compareOp = VK_COMPARE_OP_ALWAYS;
depthStencilInfo.back.compareOp = VK_COMPARE_OP_ALWAYS;
switch (m_depthTest) {
case ZTest::None:
default:
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_ALWAYS;
break;
case ZTest::LEqual:
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
break;
case ZTest::Greater:
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_GREATER;
break;
case ZTest::Equal:
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_EQUAL;
break;
case ZTest::GEqual:
depthStencilInfo.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL;
break;
}
VkPipelineColorBlendAttachmentState colorAttachment = {};
colorAttachment.blendEnable = m_dstFac != BlendFactor::Zero;
if (m_srcFac == BlendFactor::Subtract || m_dstFac == BlendFactor::Subtract) {
colorAttachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorAttachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
colorAttachment.colorBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT;
if (m_overwriteAlpha) {
colorAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
colorAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
} else {
colorAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
colorAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorAttachment.alphaBlendOp = VK_BLEND_OP_REVERSE_SUBTRACT;
}
} else {
colorAttachment.srcColorBlendFactor = BLEND_FACTOR_TABLE[int(m_srcFac)];
colorAttachment.dstColorBlendFactor = BLEND_FACTOR_TABLE[int(m_dstFac)];
colorAttachment.colorBlendOp = VK_BLEND_OP_ADD;
if (m_overwriteAlpha) {
colorAttachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
colorAttachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
} else {
colorAttachment.srcAlphaBlendFactor = BLEND_FACTOR_TABLE[int(m_srcFac)];
colorAttachment.dstAlphaBlendFactor = BLEND_FACTOR_TABLE[int(m_dstFac)];
}
colorAttachment.alphaBlendOp = VK_BLEND_OP_ADD;
}
colorAttachment.colorWriteMask =
(m_colorWrite ? (VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT) : 0) |
(m_alphaWrite ? VK_COLOR_COMPONENT_A_BIT : 0);
VkPipelineColorBlendStateCreateInfo colorBlendInfo = {};
colorBlendInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendInfo.pNext = nullptr;
colorBlendInfo.flags = 0;
colorBlendInfo.logicOpEnable = VK_FALSE;
colorBlendInfo.attachmentCount = 1;
colorBlendInfo.pAttachments = &colorAttachment;
VkGraphicsPipelineCreateInfo pipelineCreateInfo = {};
pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineCreateInfo.pNext = nullptr;
pipelineCreateInfo.flags = 0;
pipelineCreateInfo.stageCount = numStages;
pipelineCreateInfo.pStages = stages;
pipelineCreateInfo.pVertexInputState = &m_vtxFmt.m_info;
pipelineCreateInfo.pInputAssemblyState = &assemblyInfo;
pipelineCreateInfo.pTessellationState = &tessInfo;
pipelineCreateInfo.pViewportState = &viewportInfo;
pipelineCreateInfo.pRasterizationState = &rasterizationInfo;
pipelineCreateInfo.pMultisampleState = &multisampleInfo;
pipelineCreateInfo.pDepthStencilState = &depthStencilInfo;
pipelineCreateInfo.pColorBlendState = &colorBlendInfo;
pipelineCreateInfo.pDynamicState = &dynamicState;
pipelineCreateInfo.layout = m_ctx->m_pipelinelayout;
pipelineCreateInfo.renderPass = rPass;
VkPipeline p;
ThrowIfFailed(
vk::CreateGraphicsPipelines(m_ctx->m_dev, m_pipelineCache, 1, &pipelineCreateInfo, nullptr, &p));
m_pipeline = p;
m_vertex.reset();
m_fragment.reset();
m_geometry.reset();
m_control.reset();
m_evaluation.reset();
}
}
}
bool isReady() const { return m_pipeline != VK_NULL_HANDLE; }
}; };
static const VkDescriptorBufferInfo* GetBufferGPUResource(const IGraphicsBuffer* buf, int idx) { static const VkDescriptorBufferInfo* GetBufferGPUResource(const IGraphicsBuffer* buf, int idx) {
@ -3690,7 +3716,7 @@ ObjToken<IShaderStage> VulkanDataFactory::Context::newShaderStage(const uint8_t*
ObjToken<IShaderPipeline> VulkanDataFactory::Context::newShaderPipeline( ObjToken<IShaderPipeline> VulkanDataFactory::Context::newShaderPipeline(
ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment, ObjToken<IShaderStage> geometry, ObjToken<IShaderStage> vertex, ObjToken<IShaderStage> fragment, ObjToken<IShaderStage> geometry,
ObjToken<IShaderStage> control, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt, ObjToken<IShaderStage> control, ObjToken<IShaderStage> evaluation, const VertexFormatInfo& vtxFmt,
const AdditionalPipelineInfo& additionalInfo) { const AdditionalPipelineInfo& additionalInfo, bool asynchronous) {
VulkanDataFactoryImpl& factory = static_cast<VulkanDataFactoryImpl&>(m_parent); VulkanDataFactoryImpl& factory = static_cast<VulkanDataFactoryImpl&>(m_parent);
if (control || evaluation) { if (control || evaluation) {
@ -3702,7 +3728,7 @@ ObjToken<IShaderPipeline> VulkanDataFactory::Context::newShaderPipeline(
} }
return {new VulkanShaderPipeline(m_data, factory.m_ctx, vertex, fragment, geometry, control, evaluation, return {new VulkanShaderPipeline(m_data, factory.m_ctx, vertex, fragment, geometry, control, evaluation,
VK_NULL_HANDLE, vtxFmt, additionalInfo)}; VK_NULL_HANDLE, vtxFmt, additionalInfo, asynchronous)};
} }
boo::ObjToken<IShaderDataBinding> VulkanDataFactory::Context::newShaderDataBinding( boo::ObjToken<IShaderDataBinding> VulkanDataFactory::Context::newShaderDataBinding(
@ -3724,6 +3750,14 @@ void VulkanDataFactoryImpl::commitTransaction(
VulkanData* data = ctx.m_data.cast<VulkanData>(); VulkanData* data = ctx.m_data.cast<VulkanData>();
/* Start asynchronous shader compiles */
if (data->m_SPs)
for (IShaderPipeline& p : *data->m_SPs) {
auto& cp = static_cast<VulkanShaderPipeline&>(p);
if (cp.m_asynchronous)
m_pipelineQueue.addPipeline({&p});
}
/* size up resources */ /* size up resources */
VkDeviceSize constantMemSizes[3] = {}; VkDeviceSize constantMemSizes[3] = {};
VkDeviceSize texMemSize = 0; VkDeviceSize texMemSize = 0;