diff --git a/include/boo/graphicsdev/GL.hpp b/include/boo/graphicsdev/GL.hpp index c75f31f..8a0c565 100644 --- a/include/boo/graphicsdev/GL.hpp +++ b/include/boo/graphicsdev/GL.hpp @@ -46,7 +46,7 @@ public: ObjToken newShaderPipeline(ObjToken vertex, ObjToken fragment, ObjToken geometry, ObjToken control, ObjToken evaluation, const VertexFormatInfo& vtxFmt, - const AdditionalPipelineInfo& additionalInfo); + const AdditionalPipelineInfo& additionalInfo, bool asynchronous = true); ObjToken newShaderDataBinding( const ObjToken& pipeline, const ObjToken& vbo, diff --git a/include/boo/graphicsdev/IGraphicsDataFactory.hpp b/include/boo/graphicsdev/IGraphicsDataFactory.hpp index ba8a870..0b345ce 100644 --- a/include/boo/graphicsdev/IGraphicsDataFactory.hpp +++ b/include/boo/graphicsdev/IGraphicsDataFactory.hpp @@ -145,7 +145,9 @@ struct IShaderStage : IObj {}; /** Opaque token for referencing a complete graphics pipeline state necessary * 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 * and IShaderPipeline reference. Each renderable surface-material holds one @@ -241,12 +243,14 @@ struct IGraphicsDataFactory { ObjToken geometry, ObjToken control, ObjToken evaluation, const VertexFormatInfo& vtxFmt, - const AdditionalPipelineInfo& additionalInfo) = 0; + const AdditionalPipelineInfo& additionalInfo, + bool asynchronous = true) = 0; ObjToken newShaderPipeline(ObjToken vertex, ObjToken fragment, const VertexFormatInfo& vtxFmt, - const AdditionalPipelineInfo& additionalInfo) { - return newShaderPipeline(vertex, fragment, {}, {}, {}, vtxFmt, additionalInfo); + const AdditionalPipelineInfo& additionalInfo, + bool asynchronous = true) { + return newShaderPipeline(vertex, fragment, {}, {}, {}, vtxFmt, additionalInfo, asynchronous); } virtual ObjToken newShaderDataBinding( @@ -273,6 +277,7 @@ struct IGraphicsDataFactory { virtual ObjToken newPoolBuffer(BufferUse use, size_t stride, size_t count __BooTraceArgs) = 0; virtual void setDisplayGamma(float gamma) = 0; virtual bool isTessellationSupported(uint32_t& maxPatchSizeOut) = 0; + virtual void waitUntilShadersReady() = 0; }; using GraphicsDataFactoryContext = IGraphicsDataFactory::Context; diff --git a/include/boo/graphicsdev/Vulkan.hpp b/include/boo/graphicsdev/Vulkan.hpp index 4d21d40..ff4d3ae 100644 --- a/include/boo/graphicsdev/Vulkan.hpp +++ b/include/boo/graphicsdev/Vulkan.hpp @@ -155,7 +155,7 @@ public: ObjToken newShaderPipeline(ObjToken vertex, ObjToken fragment, ObjToken geometry, ObjToken control, ObjToken evaluation, const VertexFormatInfo& vtxFmt, - const AdditionalPipelineInfo& additionalInfo); + const AdditionalPipelineInfo& additionalInfo, bool asynchronous = true); boo::ObjToken newShaderDataBinding( const boo::ObjToken& pipeline, const boo::ObjToken& vbo, diff --git a/lib/graphicsdev/Common.hpp b/lib/graphicsdev/Common.hpp index 254ab86..5008664 100644 --- a/lib/graphicsdev/Common.hpp +++ b/lib/graphicsdev/Common.hpp @@ -7,6 +7,10 @@ #include #include #include +#include +#include +#include +#include #include "boo/graphicsdev/IGraphicsDataFactory.hpp" #include "../Common.hpp" @@ -196,4 +200,68 @@ struct GraphicsDataNode : ListNode, ObjToken< void UpdateGammaLUT(ITextureD* tex, float gamma); +/** Generic work-queue for asynchronously building shader pipelines on supported backends + */ +template +class PipelineCompileQueue { + struct Task { + ObjToken m_pipeline; + explicit Task(ObjToken pipeline) : m_pipeline(pipeline) {} + void run() { + m_pipeline.cast()->compile(); + } + }; + + std::queue m_tasks; + size_t m_outstandingTasks = 0; + std::vector m_threads; + std::mutex m_mt; + std::condition_variable m_cv, m_backcv; + bool m_running = true; + + void worker() { + std::unique_lock 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 pipeline) { + std::lock_guard lk(m_mt); + m_tasks.emplace(pipeline); + ++m_outstandingTasks; + m_cv.notify_one(); + } + + void waitUntilReady() { + std::unique_lock 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 diff --git a/lib/graphicsdev/GL.cpp b/lib/graphicsdev/GL.cpp index 755e1cd..d34363a 100644 --- a/lib/graphicsdev/GL.cpp +++ b/lib/graphicsdev/GL.cpp @@ -134,6 +134,8 @@ public: maxPatchSizeOut = m_maxPatchSize; return m_hasTessellation; } + + void waitUntilShadersReady() {} }; 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; } + + bool isReady() const { return true; } }; ObjToken GLDataFactory::Context::newShaderStage(const uint8_t* data, size_t size, PipelineStage stage) { @@ -979,7 +983,7 @@ ObjToken GLDataFactory::Context::newShaderStage(const uint8_t* dat ObjToken GLDataFactory::Context::newShaderPipeline( ObjToken vertex, ObjToken fragment, ObjToken geometry, ObjToken control, ObjToken evaluation, const VertexFormatInfo& vtxFmt, - const AdditionalPipelineInfo& additionalInfo) { + const AdditionalPipelineInfo& additionalInfo, bool asynchronous) { GLDataFactoryImpl& factory = static_cast(m_parent); if (control || evaluation) { diff --git a/lib/graphicsdev/Vulkan.cpp b/lib/graphicsdev/Vulkan.cpp index 93f45c8..45bbf7a 100644 --- a/lib/graphicsdev/Vulkan.cpp +++ b/lib/graphicsdev/Vulkan.cpp @@ -72,10 +72,13 @@ class VulkanDataFactoryImpl : public VulkanDataFactory, public GraphicsDataFacto friend struct VulkanPool; friend struct VulkanDescriptorPool; friend struct VulkanShaderDataBinding; + IGraphicsContext* m_parent; VulkanContext* m_ctx; VulkanDescriptorPool* m_descPoolHead = nullptr; + PipelineCompileQueue m_pipelineQueue; + float m_gamma = 1.f; ObjToken m_gammaShader; ObjToken m_gammaLUT; @@ -90,7 +93,7 @@ class VulkanDataFactoryImpl : public VulkanDataFactory, public GraphicsDataFacto const VertexElementDescriptor vfmt[] = {{VertexSemantic::Position4}, {VertexSemantic::UV4}}; AdditionalPipelineInfo info = { 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); setDisplayGamma(1.f); const struct Vert { @@ -140,6 +143,10 @@ public: maxPatchSizeOut = m_ctx->m_gpuProps.limits.maxTessellationPatchSize; return true; } + + void waitUntilShadersReady() { + m_pipelineQueue.waitUntilReady(); + } }; static inline void ThrowIfFailed(VkResult res) { @@ -489,6 +496,9 @@ void VulkanContext::initDevice() { tessellationDescriptorBit = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; 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; vk::EnumerateDeviceExtensionProperties(m_gpus[0], nullptr, &extCount, nullptr); @@ -2258,6 +2268,7 @@ public: class VulkanShaderPipeline : public GraphicsDataNode { protected: friend class VulkanDataFactory; + friend class VulkanDataFactoryImpl; friend struct VulkanShaderDataBinding; VulkanContext* m_ctx; VkPipelineCache m_pipelineCache; @@ -2277,12 +2288,13 @@ protected: bool m_overwriteAlpha; CullMode m_culling; uint32_t m_patchSize; - mutable VkPipeline m_pipeline = VK_NULL_HANDLE; + bool m_asynchronous; + mutable std::atomic m_pipeline = VK_NULL_HANDLE; VulkanShaderPipeline(const boo::ObjToken& parent, VulkanContext* ctx, ObjToken vertex, ObjToken fragment, ObjToken geometry, ObjToken control, ObjToken evaluation, VkPipelineCache pipelineCache, const VertexFormatInfo& vtxFmt, - const AdditionalPipelineInfo& info) + const AdditionalPipelineInfo& info, bool asynchronous) : GraphicsDataNode(parent) , m_ctx(ctx) , m_pipelineCache(pipelineCache) @@ -2301,7 +2313,8 @@ protected: , m_alphaWrite(info.alphaWrite) , m_overwriteAlpha(info.overwriteAlpha) , m_culling(info.culling) - , m_patchSize(info.patchSize) { + , m_patchSize(info.patchSize) + , m_asynchronous(asynchronous) { if (control && evaluation) m_prim = Primitive::Patches; } @@ -2316,226 +2329,239 @@ public: VulkanShaderPipeline& operator=(const VulkanShaderPipeline&) = delete; VulkanShaderPipeline(const VulkanShaderPipeline&) = delete; VkPipeline bind(VkRenderPass rPass = 0) const { - 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()->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()->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()->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()->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()->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(); - } + compile(rPass); + while (m_pipeline == VK_NULL_HANDLE) {} 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()->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()->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()->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()->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()->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) { @@ -3690,7 +3716,7 @@ ObjToken VulkanDataFactory::Context::newShaderStage(const uint8_t* ObjToken VulkanDataFactory::Context::newShaderPipeline( ObjToken vertex, ObjToken fragment, ObjToken geometry, ObjToken control, ObjToken evaluation, const VertexFormatInfo& vtxFmt, - const AdditionalPipelineInfo& additionalInfo) { + const AdditionalPipelineInfo& additionalInfo, bool asynchronous) { VulkanDataFactoryImpl& factory = static_cast(m_parent); if (control || evaluation) { @@ -3702,7 +3728,7 @@ ObjToken VulkanDataFactory::Context::newShaderPipeline( } 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 VulkanDataFactory::Context::newShaderDataBinding( @@ -3724,6 +3750,14 @@ void VulkanDataFactoryImpl::commitTransaction( VulkanData* data = ctx.m_data.cast(); + /* Start asynchronous shader compiles */ + if (data->m_SPs) + for (IShaderPipeline& p : *data->m_SPs) { + auto& cp = static_cast(p); + if (cp.m_asynchronous) + m_pipelineQueue.addPipeline({&p}); + } + /* size up resources */ VkDeviceSize constantMemSizes[3] = {}; VkDeviceSize texMemSize = 0;