From 4a19ac1e8352740db781012b523fefe1132672f3 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Wed, 6 Jun 2018 18:36:17 -1000 Subject: [PATCH] OpenGL and Vulkan support for tessellation shaders --- include/boo/graphicsdev/GL.hpp | 8 + .../boo/graphicsdev/IGraphicsDataFactory.hpp | 7 +- include/boo/graphicsdev/Vulkan.hpp | 25 ++ lib/graphicsdev/GL.cpp | 324 +++++++++------ lib/graphicsdev/Vulkan.cpp | 375 +++++++++++------- 5 files changed, 475 insertions(+), 264 deletions(-) diff --git a/include/boo/graphicsdev/GL.hpp b/include/boo/graphicsdev/GL.hpp index 4c9df62..9014622 100644 --- a/include/boo/graphicsdev/GL.hpp +++ b/include/boo/graphicsdev/GL.hpp @@ -54,6 +54,14 @@ public: ZTest depthTest, bool depthWrite, bool colorWrite, bool alphaWrite, CullMode culling, bool overwriteAlpha = true); + ObjToken newTessellationShaderPipeline(const char* vertSource, const char* fragSource, + const char* controlSource, const char* evaluationSource, + size_t texCount, const char** texNames, + size_t uniformBlockCount, const char** uniformBlockNames, + BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize, + ZTest depthTest, bool depthWrite, bool colorWrite, + bool alphaWrite, CullMode culling, bool overwriteAlpha = true); + ObjToken newShaderDataBinding(const ObjToken& pipeline, const ObjToken& vtxFormat, diff --git a/include/boo/graphicsdev/IGraphicsDataFactory.hpp b/include/boo/graphicsdev/IGraphicsDataFactory.hpp index 3edecb8..cc0470b 100644 --- a/include/boo/graphicsdev/IGraphicsDataFactory.hpp +++ b/include/boo/graphicsdev/IGraphicsDataFactory.hpp @@ -71,6 +71,7 @@ enum class TextureClampMode { Repeat, ClampToWhite, + ClampToBlack, ClampToEdge, ClampToEdgeNearest }; @@ -176,7 +177,8 @@ enum class PipelineStage enum class Primitive { Triangles, - TriStrips + TriStrips, + Patches /* Do not use directly, construct a tessellation pipeline instead */ }; /** Used by platform shader pipeline constructors */ @@ -293,10 +295,9 @@ struct IGraphicsDataFactory }; virtual void commitTransaction(const std::function& __BooTraceArgs)=0; - 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; }; using GraphicsDataFactoryContext = IGraphicsDataFactory::Context; diff --git a/include/boo/graphicsdev/Vulkan.hpp b/include/boo/graphicsdev/Vulkan.hpp index c1f65d8..a40fc89 100644 --- a/include/boo/graphicsdev/Vulkan.hpp +++ b/include/boo/graphicsdev/Vulkan.hpp @@ -181,6 +181,31 @@ public: colorWrite, alphaWrite, culling); } + boo::ObjToken newTessellationShaderPipeline(const char* vertSource, const char* fragSource, + const char* controlSource, const char* evaluationSource, + std::vector* vertBlobOut, + std::vector* fragBlobOut, + std::vector* controlBlobOut, + std::vector* evaluationBlobOut, + std::vector* pipelineBlob, + const boo::ObjToken& vtxFmt, + BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize, + ZTest depthTest, bool depthWrite, bool colorWrite, + bool alphaWrite, CullMode culling, bool overwriteAlpha = true); + + boo::ObjToken newTessellationShaderPipeline(const char* vertSource, const char* fragSource, + const char* controlSource, const char* evaluationSource, + const boo::ObjToken& vtxFmt, + BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize, + ZTest depthTest, bool depthWrite, bool colorWrite, + bool alphaWrite, CullMode culling) + { + return newTessellationShaderPipeline(vertSource, fragSource, controlSource, evaluationSource, + nullptr, nullptr, nullptr, nullptr, nullptr, + vtxFmt, srcFac, dstFac, patchSize, depthTest, depthWrite, + colorWrite, alphaWrite, culling); + } + boo::ObjToken newShaderDataBinding(const boo::ObjToken& pipeline, const boo::ObjToken& vtxFormat, diff --git a/lib/graphicsdev/GL.cpp b/lib/graphicsdev/GL.cpp index 9d0fce9..d149cf4 100644 --- a/lib/graphicsdev/GL.cpp +++ b/lib/graphicsdev/GL.cpp @@ -76,6 +76,9 @@ class GLDataFactoryImpl : public GLDataFactory, public GraphicsDataFactoryHead GLContext* m_glCtx; std::unordered_map> m_sharedShaders; + bool m_hasTessellation = false; + uint32_t m_maxPatchSize = 0; + float m_gamma = 1.f; ObjToken m_gammaShader; ObjToken m_gammaLUT; @@ -83,6 +86,15 @@ class GLDataFactoryImpl : public GLDataFactory, public GraphicsDataFactoryHead ObjToken m_gammaVFMT; void SetupGammaResources() { + /* Good enough place for this */ + if (GLEW_ARB_tessellation_shader) + { + m_hasTessellation = true; + GLint maxPVerts; + glGetIntegerv(GL_MAX_PATCH_VERTICES, &maxPVerts); + m_maxPatchSize = uint32_t(maxPVerts); + } + commitTransaction([this](IGraphicsDataFactory::Context& ctx) { const char* texNames[] = {"screenTex", "gammaLUT"}; @@ -125,6 +137,14 @@ public: if (gamma != 1.f) UpdateGammaLUT(m_gammaLUT.get(), gamma); } + + bool isTessellationSupported(uint32_t& maxPatchSizeOut) + { + maxPatchSizeOut = m_maxPatchSize; + return m_hasTessellation; + } + + GLShareableShader::Token PrepareShaderStage(const char* source, GLenum stage); }; static const GLenum USE_TABLE[] = @@ -250,6 +270,15 @@ static void SetClampMode(GLenum target, TextureClampMode clampMode) glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color); break; } + case TextureClampMode::ClampToBlack: + { + glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + glTexParameteri(target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + GLfloat color[] = {0.f, 0.f, 0.f, 1.f}; + glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, color); + break; + } case TextureClampMode::ClampToEdge: { glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); @@ -653,8 +682,32 @@ GLDataFactory::Context::newStaticArrayTexture(size_t width, size_t height, size_ factory.m_glCtx->m_anisotropy, data, sz)}; } +static const GLenum PRIMITIVE_TABLE[] = +{ + GL_TRIANGLES, + GL_TRIANGLE_STRIP, + GL_PATCHES +}; + +static const GLenum BLEND_FACTOR_TABLE[] = +{ + GL_ZERO, + GL_ONE, + GL_SRC_COLOR, + GL_ONE_MINUS_SRC_COLOR, + GL_DST_COLOR, + GL_ONE_MINUS_DST_COLOR, + GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, + GL_DST_ALPHA, + GL_ONE_MINUS_DST_ALPHA, + GL_SRC1_COLOR, + GL_ONE_MINUS_SRC1_COLOR +}; + class GLShaderPipeline : public GraphicsDataNode { +protected: friend class GLDataFactory; friend struct GLCommandQueue; friend struct GLShaderDataBinding; @@ -674,11 +727,50 @@ class GLShaderPipeline : public GraphicsDataNode mutable std::vector m_uniLocs; mutable std::vector m_texNames; mutable std::vector m_blockNames; - GLShaderPipeline(const ObjToken& parent) - : GraphicsDataNode(parent) {} + GLShaderPipeline(const ObjToken& parent, + size_t texCount, const char** texNames, + size_t uniformBlockCount, const char** uniformBlockNames, + BlendFactor srcFac, BlendFactor dstFac, Primitive prim, + ZTest depthTest, bool depthWrite, bool colorWrite, + bool alphaWrite, CullMode culling, bool overwriteAlpha) + : GraphicsDataNode(parent) + { + m_texNames.reserve(texCount); + for (int i=0 ; i& parent, + size_t texCount, const char** texNames, + size_t uniformBlockCount, const char** uniformBlockNames, + BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize, + ZTest depthTest, bool depthWrite, bool colorWrite, + bool alphaWrite, CullMode culling, bool overwriteAlpha) + : GLShaderPipeline(parent, texCount, texNames, uniformBlockCount, uniformBlockNames, + srcFac, dstFac, Primitive::Patches, depthTest, depthWrite, colorWrite, + alphaWrite, culling, overwriteAlpha), m_patchSize(patchSize) + {} +public: + ~GLTessellationShaderPipeline() = default; + + void attachExtraStages() const + { + glAttachShader(m_prog, m_control.get().m_shader); + glAttachShader(m_prog, m_evaluation.get().m_shader); + } + + void resetExtraStages() const + { + glDetachShader(m_prog, m_control.get().m_shader); + glDetachShader(m_prog, m_evaluation.get().m_shader); + m_control.reset(); + m_evaluation.reset(); + } + + void setExtraParameters() const + { + glPatchParameteri(GL_PATCH_VERTICES, m_patchSize); + } }; -static const GLenum BLEND_FACTOR_TABLE[] = +GLShareableShader::Token GLDataFactoryImpl::PrepareShaderStage(const char* source, GLenum stage) { - GL_ZERO, - GL_ONE, - GL_SRC_COLOR, - GL_ONE_MINUS_SRC_COLOR, - GL_DST_COLOR, - GL_ONE_MINUS_DST_COLOR, - GL_SRC_ALPHA, - GL_ONE_MINUS_SRC_ALPHA, - GL_DST_ALPHA, - GL_ONE_MINUS_DST_ALPHA, - GL_SRC1_COLOR, - GL_ONE_MINUS_SRC1_COLOR -}; + XXH64_state_t hashState; + XXH64_reset(&hashState, 0); + XXH64_update(&hashState, source, strlen(source)); + uint64_t hash = XXH64_digest(&hashState); + + GLint status; + auto search = m_sharedShaders.find(hash); + if (search != m_sharedShaders.end()) + { + return search->second->lock(); + } + else + { + GLuint sobj = glCreateShader(stage); + if (!sobj) + { + Log.report(logvisor::Fatal, "unable to create shader"); + return {}; + } + + glShaderSource(sobj, 1, &source, nullptr); + glCompileShader(sobj); + glGetShaderiv(sobj, GL_COMPILE_STATUS, &status); + if (status != GL_TRUE) + { + GLint logLen; + glGetShaderiv(sobj, GL_INFO_LOG_LENGTH, &logLen); + std::unique_ptr log(new char[logLen]); + glGetShaderInfoLog(sobj, logLen, nullptr, log.get()); + Log.report(logvisor::Fatal, "unable to compile source\n%s\n%s\n", log.get(), source); + return {}; + } + + auto it = + m_sharedShaders.emplace(std::make_pair(hash, + std::make_unique(*this, hash, sobj))).first; + return it->second->lock(); + } +} ObjToken GLDataFactory::Context::newShaderPipeline (const char* vertSource, const char* fragSource, @@ -830,112 +987,43 @@ ObjToken GLDataFactory::Context::newShaderPipeline bool alphaWrite, CullMode culling, bool overwriteAlpha) { GLDataFactoryImpl& factory = static_cast(m_parent); - ObjToken retval(new GLShaderPipeline(m_data)); + ObjToken retval(new GLShaderPipeline( + m_data, texCount, texNames, uniformBlockCount, uniformBlockNames, srcFac, dstFac, prim, + depthTest, depthWrite, colorWrite, alphaWrite, culling, overwriteAlpha)); GLShaderPipeline& shader = *retval.cast(); - XXH64_state_t hashState; - uint64_t hashes[2]; - XXH64_reset(&hashState, 0); - XXH64_update(&hashState, vertSource, strlen(vertSource)); - hashes[0] = XXH64_digest(&hashState); - XXH64_reset(&hashState, 0); - XXH64_update(&hashState, fragSource, strlen(fragSource)); - hashes[1] = XXH64_digest(&hashState); + shader.m_vert = factory.PrepareShaderStage(vertSource, GL_VERTEX_SHADER); + shader.m_frag = factory.PrepareShaderStage(fragSource, GL_FRAGMENT_SHADER); - GLint status; - auto vertFind = factory.m_sharedShaders.find(hashes[0]); - if (vertFind != factory.m_sharedShaders.end()) - { - shader.m_vert = vertFind->second->lock(); - } - else - { - GLuint sobj = glCreateShader(GL_VERTEX_SHADER); - if (!sobj) - { - Log.report(logvisor::Fatal, "unable to create vert shader"); - return {}; - } + return retval; +} - glShaderSource(sobj, 1, &vertSource, nullptr); - glCompileShader(sobj); - glGetShaderiv(sobj, GL_COMPILE_STATUS, &status); - if (status != GL_TRUE) - { - GLint logLen; - glGetShaderiv(sobj, GL_INFO_LOG_LENGTH, &logLen); - std::unique_ptr log(new char[logLen]); - glGetShaderInfoLog(sobj, logLen, nullptr, log.get()); - Log.report(logvisor::Fatal, "unable to compile vert source\n%s\n%s\n", log.get(), vertSource); - return {}; - } +ObjToken GLDataFactory::Context::newTessellationShaderPipeline +(const char* vertSource, const char* fragSource, + const char* controlSource, const char* evaluationSource, + size_t texCount, const char** texNames, + size_t uniformBlockCount, const char** uniformBlockNames, + BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize, + ZTest depthTest, bool depthWrite, bool colorWrite, + bool alphaWrite, CullMode culling, bool overwriteAlpha) +{ + GLDataFactoryImpl& factory = static_cast(m_parent); - auto it = - factory.m_sharedShaders.emplace(std::make_pair(hashes[0], - std::make_unique(factory, hashes[0], sobj))).first; - shader.m_vert = it->second->lock(); - } - auto fragFind = factory.m_sharedShaders.find(hashes[1]); - if (fragFind != factory.m_sharedShaders.end()) - { - shader.m_frag = fragFind->second->lock(); - } - else - { - GLuint sobj = glCreateShader(GL_FRAGMENT_SHADER); - if (!sobj) - { - Log.report(logvisor::Fatal, "unable to create frag shader"); - return {}; - } + if (!factory.m_hasTessellation) + Log.report(logvisor::Fatal, "Device does not support tessellation shaders"); + if (patchSize > factory.m_maxPatchSize) + Log.report(logvisor::Fatal, "Device supports %d patch vertices, %d requested", + int(factory.m_maxPatchSize), int(patchSize)); - glShaderSource(sobj, 1, &fragSource, nullptr); - glCompileShader(sobj); - glGetShaderiv(sobj, GL_COMPILE_STATUS, &status); - if (status != GL_TRUE) - { - GLint logLen; - glGetShaderiv(sobj, GL_INFO_LOG_LENGTH, &logLen); - std::unique_ptr log(new char[logLen]); - glGetShaderInfoLog(sobj, logLen, nullptr, log.get()); - Log.report(logvisor::Fatal, "unable to compile frag source\n%s\n%s\n", log.get(), fragSource); - return {}; - } + ObjToken retval(new GLTessellationShaderPipeline( + m_data, texCount, texNames, uniformBlockCount, uniformBlockNames, srcFac, dstFac, patchSize, + depthTest, depthWrite, colorWrite, alphaWrite, culling, overwriteAlpha)); + GLTessellationShaderPipeline& shader = *retval.cast(); - auto it = - factory.m_sharedShaders.emplace(std::make_pair(hashes[1], - std::make_unique(factory, hashes[1], sobj))).first; - shader.m_frag = it->second->lock(); - } - - shader.m_texNames.reserve(texCount); - for (int i=0 ; i& out, const char* vertSource, uint64_t srcKey); - uint64_t CompileFrag(std::vector& out, const char* fragSource, uint64_t srcKey); + bool isTessellationSupported(uint32_t& maxPatchSizeOut) + { + maxPatchSizeOut = 0; + if (!m_ctx->m_features.tessellationShader) + return false; + maxPatchSizeOut = m_ctx->m_gpuProps.limits.maxTessellationPatchSize; + return true; + } + + VulkanShareableShader::Token PrepareShaderStage(const char* source, std::vector* blobOut, + EShLanguage lang); + uint64_t Compile(std::vector& out, const char* source, uint64_t srcKey, EShLanguage lang); }; static inline void ThrowIfFailed(VkResult res) @@ -540,6 +550,12 @@ void VulkanContext::initDevice() Log.report(logvisor::Fatal, "Vulkan device does not support DXT-format textures"); features.textureCompressionBC = VK_TRUE; + VkShaderStageFlagBits tessellationDescriptorBit = VkShaderStageFlagBits(0); + if (m_features.tessellationShader) + { + tessellationDescriptorBit = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; + features.tessellationShader = VK_TRUE; + } uint32_t extCount = 0; vk::EnumerateDeviceExtensionProperties(m_gpus[0], nullptr, &extCount, nullptr); @@ -651,7 +667,8 @@ void VulkanContext::initDevice() layoutBindings[i].binding = i; layoutBindings[i].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; layoutBindings[i].descriptorCount = 1; - layoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + layoutBindings[i].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT | + tessellationDescriptorBit; layoutBindings[i].pImmutableSamplers = nullptr; } for (int i=BOO_GLSL_MAX_UNIFORM_COUNT ; i static const VkPrimitiveTopology PRIMITIVE_TABLE[] = { VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, - VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP + VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, + VK_PRIMITIVE_TOPOLOGY_PATCH_LIST }; static const VkBlendFactor BLEND_FACTOR_TABLE[] = @@ -2165,6 +2189,7 @@ static const VkBlendFactor BLEND_FACTOR_TABLE[] = class VulkanShaderPipeline : public GraphicsDataNode { +protected: friend class VulkanDataFactory; friend struct VulkanShaderDataBinding; VulkanContext* m_ctx; @@ -2208,6 +2233,9 @@ public: } VulkanShaderPipeline& operator=(const VulkanShaderPipeline&) = delete; VulkanShaderPipeline(const VulkanShaderPipeline&) = delete; + virtual uint32_t defineExtraStages(VkPipelineShaderStageCreateInfo* stages) const { return 0; } + virtual const VkPipelineTessellationStateCreateInfo* getTessellationInfo() const { return nullptr; } + virtual void resetExtraStages() const {} VkPipeline bind(VkRenderPass rPass = 0) const { if (!m_pipeline) @@ -2237,7 +2265,7 @@ public: dynamicState.pDynamicStates = dynamicStateEnables; dynamicState.dynamicStateCount = 0; - VkPipelineShaderStageCreateInfo stages[2] = {}; + VkPipelineShaderStageCreateInfo stages[4] = {}; stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; stages[0].pNext = nullptr; @@ -2255,6 +2283,8 @@ public: stages[1].pName = "main"; stages[1].pSpecializationInfo = nullptr; + uint32_t extraStages = defineExtraStages(&stages[2]); + VkPipelineInputAssemblyStateCreateInfo assemblyInfo = {}; assemblyInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; assemblyInfo.pNext = nullptr; @@ -2378,10 +2408,11 @@ public: pipelineCreateInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipelineCreateInfo.pNext = nullptr; pipelineCreateInfo.flags = 0; - pipelineCreateInfo.stageCount = 2; + pipelineCreateInfo.stageCount = 2 + extraStages; pipelineCreateInfo.pStages = stages; pipelineCreateInfo.pVertexInputState = &m_vtxFmt.cast()->m_info; pipelineCreateInfo.pInputAssemblyState = &assemblyInfo; + pipelineCreateInfo.pTessellationState = getTessellationInfo(); pipelineCreateInfo.pViewportState = &viewportInfo; pipelineCreateInfo.pRasterizationState = &rasterizationInfo; pipelineCreateInfo.pMultisampleState = &multisampleInfo; @@ -2396,11 +2427,79 @@ public: m_vert.reset(); m_frag.reset(); + resetExtraStages(); } return m_pipeline; } }; +class VulkanTessellationShaderPipeline : public VulkanShaderPipeline +{ + friend class VulkanDataFactory; + friend struct VulkanShaderDataBinding; + + mutable VulkanShareableShader::Token m_control; + mutable VulkanShareableShader::Token m_evaluation; + + VkPipelineTessellationStateCreateInfo m_tessInfo; + + VulkanTessellationShaderPipeline(const boo::ObjToken& parent, + VulkanContext* ctx, + VulkanShareableShader::Token&& vert, + VulkanShareableShader::Token&& frag, + VulkanShareableShader::Token&& control, + VulkanShareableShader::Token&& evaluation, + VkPipelineCache pipelineCache, + const boo::ObjToken& vtxFmt, + BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize, + ZTest depthTest, bool depthWrite, bool colorWrite, + bool alphaWrite, bool overwriteAlpha, CullMode culling) + : VulkanShaderPipeline(parent, ctx, std::move(vert), std::move(frag), pipelineCache, vtxFmt, srcFac, dstFac, + Primitive::Patches, depthTest, depthWrite, colorWrite, alphaWrite, overwriteAlpha, culling), + m_control(std::move(control)), m_evaluation(std::move(evaluation)) + { + m_tessInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO; + m_tessInfo.pNext = nullptr; + m_tessInfo.flags = 0; + m_tessInfo.patchControlPoints = patchSize; + } + +public: + ~VulkanTessellationShaderPipeline() = default; + + uint32_t defineExtraStages(VkPipelineShaderStageCreateInfo* stages) const + { + stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stages[0].pNext = nullptr; + stages[0].flags = 0; + stages[0].stage = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT; + stages[0].module = m_control.get().m_shader; + stages[0].pName = "main"; + stages[0].pSpecializationInfo = nullptr; + + stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stages[1].pNext = nullptr; + stages[1].flags = 0; + stages[1].stage = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT; + stages[1].module = m_evaluation.get().m_shader; + stages[1].pName = "main"; + stages[1].pSpecializationInfo = nullptr; + + return 2; + } + + const VkPipelineTessellationStateCreateInfo* getTessellationInfo() const + { + return &m_tessInfo; + } + + void resetExtraStages() const + { + m_control.reset(); + m_evaluation.reset(); + } +}; + static const VkDescriptorBufferInfo* GetBufferGPUResource(const IGraphicsBuffer* buf, int idx) { if (buf->dynamic()) @@ -3474,52 +3573,86 @@ void VulkanTextureD::unmap() VulkanDataFactoryImpl::VulkanDataFactoryImpl(IGraphicsContext* parent, VulkanContext* ctx) : m_parent(parent), m_ctx(ctx) {} -uint64_t VulkanDataFactoryImpl::CompileVert(std::vector& out, const char* vertSource, uint64_t srcKey) +VulkanShareableShader::Token VulkanDataFactoryImpl::PrepareShaderStage(const char* source, + std::vector* blobOut, + EShLanguage lang) { - const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules); - glslang::TShader vs(EShLangVertex); - vs.setStrings(&vertSource, 1); - if (!vs.parse(&glslang::DefaultTBuiltInResource, 110, false, messages)) - { - printf("%s\n", vertSource); - Log.report(logvisor::Fatal, "unable to compile vertex shader\n%s", vs.getInfoLog()); - } - - glslang::TProgram prog; - prog.addShader(&vs); - if (!prog.link(messages)) - { - Log.report(logvisor::Fatal, "unable to link shader program\n%s", prog.getInfoLog()); - } - glslang::GlslangToSpv(*prog.getIntermediate(EShLangVertex), out); - //spv::Disassemble(std::cerr, out); - + uint64_t srcHash = 0; + uint64_t binHash = 0; XXH64_state_t hashState; XXH64_reset(&hashState, 0); - XXH64_update(&hashState, out.data(), out.size() * sizeof(unsigned int)); - uint64_t binKey = XXH64_digest(&hashState); - m_sourceToBinary[srcKey] = binKey; - return binKey; + if (source) + { + XXH64_update(&hashState, source, strlen(source)); + srcHash = XXH64_digest(&hashState); + auto binSearch = m_sourceToBinary.find(srcHash); + if (binSearch != m_sourceToBinary.cend()) + binHash = binSearch->second; + } + else if (blobOut && blobOut->size()) + { + XXH64_update(&hashState, blobOut->data(), blobOut->size() * sizeof(unsigned int)); + binHash = XXH64_digest(&hashState); + } + + if (blobOut && blobOut->empty()) + binHash = Compile(*blobOut, source, srcHash, lang); + + auto search = binHash ? m_sharedShaders.find(binHash) : m_sharedShaders.end(); + if (search != m_sharedShaders.end()) + { + return search->second->lock(); + } + else + { + std::vector blob; + const std::vector* useBlob; + if (blobOut) + { + useBlob = blobOut; + } + else + { + useBlob = &blob; + binHash = Compile(blob, source, srcHash, lang); + } + + VkShaderModuleCreateInfo smCreateInfo = {}; + smCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + smCreateInfo.pNext = nullptr; + smCreateInfo.flags = 0; + + VkShaderModule module; + smCreateInfo.codeSize = useBlob->size() * sizeof(unsigned int); + smCreateInfo.pCode = useBlob->data(); + ThrowIfFailed(vk::CreateShaderModule(m_ctx->m_dev, &smCreateInfo, nullptr, &module)); + + auto it = + m_sharedShaders.emplace(std::make_pair(binHash, + std::make_unique(*this, srcHash, binHash, module))).first; + return it->second->lock(); + } } -uint64_t VulkanDataFactoryImpl::CompileFrag(std::vector& out, const char* fragSource, uint64_t srcKey) +uint64_t VulkanDataFactoryImpl::Compile(std::vector& out, const char* source, + uint64_t srcKey, EShLanguage lang) { const EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules); - glslang::TShader fs(EShLangFragment); - fs.setStrings(&fragSource, 1); - if (!fs.parse(&glslang::DefaultTBuiltInResource, 110, false, messages)) + glslang::TShader shader(lang); + shader.setStrings(&source, 1); + if (!shader.parse(&glslang::DefaultTBuiltInResource, 110, false, messages)) { - printf("%s\n", fragSource); - Log.report(logvisor::Fatal, "unable to compile fragment shader\n%s", fs.getInfoLog()); + printf("%s\n", source); + Log.report(logvisor::Fatal, "unable to compile shader\n%s", shader.getInfoLog()); } glslang::TProgram prog; - prog.addShader(&fs); + prog.addShader(&shader); if (!prog.link(messages)) { Log.report(logvisor::Fatal, "unable to link shader program\n%s", prog.getInfoLog()); } - glslang::GlslangToSpv(*prog.getIntermediate(EShLangFragment), out); + glslang::GlslangToSpv(*prog.getIntermediate(lang), out); //spv::Disassemble(std::cerr, out); XXH64_state_t hashState; @@ -3540,111 +3673,8 @@ boo::ObjToken VulkanDataFactory::Context::newShaderPipeline { VulkanDataFactoryImpl& factory = static_cast(m_parent); - XXH64_state_t hashState; - uint64_t srcHashes[2] = {}; - uint64_t binHashes[2] = {}; - XXH64_reset(&hashState, 0); - if (vertSource) - { - XXH64_update(&hashState, vertSource, strlen(vertSource)); - srcHashes[0] = XXH64_digest(&hashState); - auto binSearch = factory.m_sourceToBinary.find(srcHashes[0]); - if (binSearch != factory.m_sourceToBinary.cend()) - binHashes[0] = binSearch->second; - } - else if (vertBlobOut && vertBlobOut->size()) - { - XXH64_update(&hashState, vertBlobOut->data(), vertBlobOut->size() * sizeof(unsigned int)); - binHashes[0] = XXH64_digest(&hashState); - } - XXH64_reset(&hashState, 0); - if (fragSource) - { - XXH64_update(&hashState, fragSource, strlen(fragSource)); - srcHashes[1] = XXH64_digest(&hashState); - auto binSearch = factory.m_sourceToBinary.find(srcHashes[1]); - if (binSearch != factory.m_sourceToBinary.cend()) - binHashes[1] = binSearch->second; - } - else if (fragBlobOut && fragBlobOut->size()) - { - XXH64_update(&hashState, fragBlobOut->data(), fragBlobOut->size() * sizeof(unsigned int)); - binHashes[1] = XXH64_digest(&hashState); - } - - if (vertBlobOut && vertBlobOut->empty()) - binHashes[0] = factory.CompileVert(*vertBlobOut, vertSource, srcHashes[0]); - - if (fragBlobOut && fragBlobOut->empty()) - binHashes[1] = factory.CompileFrag(*fragBlobOut, fragSource, srcHashes[1]); - - VkShaderModuleCreateInfo smCreateInfo = {}; - smCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - smCreateInfo.pNext = nullptr; - smCreateInfo.flags = 0; - - VulkanShareableShader::Token vertShader; - VulkanShareableShader::Token fragShader; - auto vertFind = binHashes[0] ? factory.m_sharedShaders.find(binHashes[0]) : - factory.m_sharedShaders.end(); - if (vertFind != factory.m_sharedShaders.end()) - { - vertShader = vertFind->second->lock(); - } - else - { - std::vector vertBlob; - const std::vector* useVertBlob; - if (vertBlobOut) - { - useVertBlob = vertBlobOut; - } - else - { - useVertBlob = &vertBlob; - binHashes[0] = factory.CompileVert(vertBlob, vertSource, srcHashes[0]); - } - - VkShaderModule vertModule; - smCreateInfo.codeSize = useVertBlob->size() * sizeof(unsigned int); - smCreateInfo.pCode = useVertBlob->data(); - ThrowIfFailed(vk::CreateShaderModule(factory.m_ctx->m_dev, &smCreateInfo, nullptr, &vertModule)); - - auto it = - factory.m_sharedShaders.emplace(std::make_pair(binHashes[0], - std::make_unique(factory, srcHashes[0], binHashes[0], vertModule))).first; - vertShader = it->second->lock(); - } - auto fragFind = binHashes[1] ? factory.m_sharedShaders.find(binHashes[1]) : - factory.m_sharedShaders.end(); - if (fragFind != factory.m_sharedShaders.end()) - { - fragShader = fragFind->second->lock(); - } - else - { - std::vector fragBlob; - const std::vector* useFragBlob; - if (fragBlobOut) - { - useFragBlob = fragBlobOut; - } - else - { - useFragBlob = &fragBlob; - binHashes[1] = factory.CompileFrag(fragBlob, fragSource, srcHashes[1]); - } - - VkShaderModule fragModule; - smCreateInfo.codeSize = useFragBlob->size() * sizeof(unsigned int); - smCreateInfo.pCode = useFragBlob->data(); - ThrowIfFailed(vk::CreateShaderModule(factory.m_ctx->m_dev, &smCreateInfo, nullptr, &fragModule)); - - auto it = - factory.m_sharedShaders.emplace(std::make_pair(binHashes[1], - std::make_unique(factory, srcHashes[1], binHashes[1], fragModule))).first; - fragShader = it->second->lock(); - } + VulkanShareableShader::Token vertShader = factory.PrepareShaderStage(vertSource, vertBlobOut, EShLangVertex); + VulkanShareableShader::Token fragShader = factory.PrepareShaderStage(fragSource, fragBlobOut, EShLangFragment); VkPipelineCache pipelineCache = VK_NULL_HANDLE; if (pipelineBlob) @@ -3681,6 +3711,65 @@ boo::ObjToken VulkanDataFactory::Context::newShaderPipeline return {retval}; } +boo::ObjToken VulkanDataFactory::Context::newTessellationShaderPipeline +(const char* vertSource, const char* fragSource, const char* controlSource, const char* evaluationSource, + std::vector* vertBlobOut, std::vector* fragBlobOut, + std::vector* controlBlobOut, std::vector* evaluationBlobOut, + std::vector* pipelineBlob, const boo::ObjToken& vtxFmt, + BlendFactor srcFac, BlendFactor dstFac, uint32_t patchSize, + ZTest depthTest, bool depthWrite, bool colorWrite, + bool alphaWrite, CullMode culling, bool overwriteAlpha) +{ + VulkanDataFactoryImpl& factory = static_cast(m_parent); + + if (!factory.m_ctx->m_features.tessellationShader) + Log.report(logvisor::Fatal, "Device does not support tessellation shaders"); + if (patchSize > factory.m_ctx->m_gpuProps.limits.maxTessellationPatchSize) + Log.report(logvisor::Fatal, "Device supports %d patch vertices, %d requested", + int(factory.m_ctx->m_gpuProps.limits.maxTessellationPatchSize), int(patchSize)); + + VulkanShareableShader::Token vertShader = factory.PrepareShaderStage(vertSource, vertBlobOut, EShLangVertex); + VulkanShareableShader::Token fragShader = factory.PrepareShaderStage(fragSource, fragBlobOut, EShLangFragment); + VulkanShareableShader::Token controlShader = factory.PrepareShaderStage(controlSource, controlBlobOut, EShLangTessControl); + VulkanShareableShader::Token evaluationShader = factory.PrepareShaderStage(evaluationSource, evaluationBlobOut, EShLangTessEvaluation); + + VkPipelineCache pipelineCache = VK_NULL_HANDLE; + if (pipelineBlob) + { + VkPipelineCacheCreateInfo cacheDataInfo = {}; + cacheDataInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + cacheDataInfo.pNext = nullptr; + + cacheDataInfo.initialDataSize = pipelineBlob->size(); + if (cacheDataInfo.initialDataSize) + cacheDataInfo.pInitialData = pipelineBlob->data(); + + ThrowIfFailed(vk::CreatePipelineCache(factory.m_ctx->m_dev, &cacheDataInfo, nullptr, &pipelineCache)); + } + + VulkanShaderPipeline* retval = + new VulkanTessellationShaderPipeline(m_data, factory.m_ctx, std::move(vertShader), + std::move(fragShader), std::move(controlShader), + std::move(evaluationShader), pipelineCache, vtxFmt, srcFac, + dstFac, patchSize, depthTest, depthWrite, colorWrite, + alphaWrite, overwriteAlpha, culling); + + if (pipelineBlob && pipelineBlob->empty()) + { + size_t cacheSz = 0; + ThrowIfFailed(vk::GetPipelineCacheData(factory.m_ctx->m_dev, pipelineCache, &cacheSz, nullptr)); + if (cacheSz) + { + pipelineBlob->resize(cacheSz); + ThrowIfFailed(vk::GetPipelineCacheData(factory.m_ctx->m_dev, pipelineCache, + &cacheSz, pipelineBlob->data())); + pipelineBlob->resize(cacheSz); + } + } + + return {retval}; +} + VulkanDataFactory::Context::Context(VulkanDataFactory& parent __BooTraceArgs) : m_parent(parent), m_data(new VulkanData(static_cast(parent) __BooTraceArgsUse)) {} VulkanDataFactory::Context::~Context() {}