From d16caa71af444ee089a9ac74ce76fa3176eb3867 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sat, 1 Oct 2022 20:01:23 -0700 Subject: [PATCH] Vulkan: Add geometry shader ID tracking. We're still not generating them, yet. But this tracks the objects and IDs through the pipeline. --- Common/GPU/Vulkan/VulkanRenderManager.cpp | 11 ++- Common/GPU/Vulkan/VulkanRenderManager.h | 1 + GPU/Common/ShaderId.cpp | 39 ++++++++++ GPU/Common/ShaderId.h | 39 ++++++++++ GPU/GPUState.h | 3 +- GPU/Vulkan/DrawEngineVulkan.cpp | 13 ++-- GPU/Vulkan/DrawEngineVulkan.h | 4 +- GPU/Vulkan/GPU_Vulkan.cpp | 1 + GPU/Vulkan/PipelineManagerVulkan.cpp | 21 +++-- GPU/Vulkan/PipelineManagerVulkan.h | 4 +- GPU/Vulkan/ShaderManagerVulkan.cpp | 95 +++++++++++++++++++++-- GPU/Vulkan/ShaderManagerVulkan.h | 32 +++++++- UI/DevScreens.cpp | 2 +- 13 files changed, 241 insertions(+), 24 deletions(-) diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index 4e047bf64e..344b059bbc 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -30,8 +30,9 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR // Fill in the last part of the desc since now it's time to block. VkShaderModule vs = desc->vertexShader->BlockUntilReady(); VkShaderModule fs = desc->fragmentShader->BlockUntilReady(); + VkShaderModule gs = desc->geometryShader ? desc->geometryShader->BlockUntilReady() : VK_NULL_HANDLE; - if (!vs || !fs) { + if (!vs || !fs || (!gs && desc->geometryShader)) { ERROR_LOG(G3D, "Failed creating graphics pipeline - missing shader modules"); // We're kinda screwed here? return false; @@ -49,6 +50,14 @@ bool VKRGraphicsPipeline::Create(VulkanContext *vulkan, VkRenderPass compatibleR ss[1].pSpecializationInfo = nullptr; ss[1].module = fs; ss[1].pName = "main"; + if (gs) { + stageCount++; + ss[2].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + ss[2].stage = VK_SHADER_STAGE_GEOMETRY_BIT; + ss[2].pSpecializationInfo = nullptr; + ss[2].module = gs; + ss[2].pName = "main"; + } VkGraphicsPipelineCreateInfo pipe{ VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; pipe.pStages = ss; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 66692d99a7..7fd45e2246 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -128,6 +128,7 @@ struct VKRGraphicsPipelineDesc { // Replaced the ShaderStageInfo with promises here so we can wait for compiles to finish. Promise *vertexShader = nullptr; Promise *fragmentShader = nullptr; + Promise *geometryShader = nullptr; VkPipelineInputAssemblyStateCreateInfo inputAssembly{ VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; VkVertexInputAttributeDescription attrs[8]{}; diff --git a/GPU/Common/ShaderId.cpp b/GPU/Common/ShaderId.cpp index bb8dd54237..eccd5d158f 100644 --- a/GPU/Common/ShaderId.cpp +++ b/GPU/Common/ShaderId.cpp @@ -366,3 +366,42 @@ void ComputeFragmentShaderID(FShaderID *id_out, const ComputedPipelineState &pip *id_out = id; } + +std::string GeometryShaderDesc(const GShaderID &id) { + std::stringstream desc; + desc << StringFromFormat("%08x:%08x ", id.d[1], id.d[0]); + if (id.Bit(GS_BIT_ENABLED)) desc << "ENABLED "; + if (id.Bit(GS_BIT_DO_TEXTURE)) desc << "TEX "; + if (id.Bit(GS_BIT_LMODE)) desc << "LMODE "; + return desc.str(); +} + +void ComputeGeometryShaderID(GShaderID *id_out, const Draw::Bugs &bugs, int prim) { + GShaderID id; + + bool vertexRangeCulling = + !gstate.isModeThrough() && gstate_c.submitType == SubmitType::DRAW; // neither hw nor sw spline/bezier. See #11692 + + // If we're not using GS culling, return a zero ID. + // Also, only use this for triangle primitives. + if (!vertexRangeCulling || !gstate_c.Supports(GPU_SUPPORTS_GS_CULLING) || (prim != GE_PRIM_TRIANGLES && prim != GE_PRIM_TRIANGLE_FAN && prim != GE_PRIM_TRIANGLE_STRIP)) { + *id_out = id; + return; + } + + id.SetBit(GS_BIT_ENABLED, true); + + if (gstate.isModeClear()) { + // No attribute bits. + } else { + bool isModeThrough = gstate.isModeThrough(); + bool lmode = gstate.isUsingSecondaryColor() && gstate.isLightingEnabled() && !isModeThrough; + + id.SetBit(GS_BIT_LMODE, lmode); + if (gstate.isTextureMapEnabled()) { + id.SetBit(GS_BIT_DO_TEXTURE); + } + } + + *id_out = id; +} diff --git a/GPU/Common/ShaderId.h b/GPU/Common/ShaderId.h index 20a6cfda48..ec25abb4d3 100644 --- a/GPU/Common/ShaderId.h +++ b/GPU/Common/ShaderId.h @@ -104,6 +104,17 @@ static inline FShaderBit operator +(FShaderBit bit, int i) { return FShaderBit((int)bit + i); } +// Some of these bits are straight from FShaderBit, since they essentially enable attributes directly. +enum GShaderBit : uint8_t { + GS_BIT_ENABLED = 0, // If not set, we don't use a geo shader. + GS_BIT_DO_TEXTURE = 1, // presence of texcoords + GS_BIT_LMODE = 2, // presence of specular color (regular color always present) +}; + +static inline GShaderBit operator +(GShaderBit bit, int i) { + return GShaderBit((int)bit + i); +} + struct ShaderID { ShaderID() { clear(); @@ -232,6 +243,31 @@ struct FShaderID : ShaderID { } }; +struct GShaderID : ShaderID { + GShaderID() : ShaderID() { + } + + explicit GShaderID(ShaderID &src) { + memcpy(d, src.d, sizeof(d)); + } + + bool Bit(GShaderBit bit) const { + return ShaderID::Bit((int)bit); + } + + int Bits(GShaderBit bit, int count) const { + return ShaderID::Bits((int)bit, count); + } + + void SetBit(GShaderBit bit, bool value = true) { + ShaderID::SetBit((int)bit, value); + } + + void SetBits(GShaderBit bit, int count, int value) { + ShaderID::SetBits((int)bit, count, value); + } +}; + namespace Draw { class Bugs; } @@ -244,3 +280,6 @@ std::string VertexShaderDesc(const VShaderID &id); struct ComputedPipelineState; void ComputeFragmentShaderID(FShaderID *id, const ComputedPipelineState &pipelineState, const Draw::Bugs &bugs); std::string FragmentShaderDesc(const FShaderID &id); + +void ComputeGeometryShaderID(GShaderID *id, const Draw::Bugs &bugs, int prim); +std::string GeometryShaderDesc(const GShaderID &id); diff --git a/GPU/GPUState.h b/GPU/GPUState.h index a10cfe790e..e5d5662845 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -486,7 +486,8 @@ enum { // Free bit: 15 GPU_SUPPORTS_DEPTH_TEXTURE = FLAG_BIT(16), GPU_SUPPORTS_ACCURATE_DEPTH = FLAG_BIT(17), - // Free bits: 18-19 + GPU_SUPPORTS_GS_CULLING = FLAG_BIT(18), // Geometry shader + // Free bit: 19 GPU_SUPPORTS_ANY_FRAMEBUFFER_FETCH = FLAG_BIT(20), GPU_SCALE_DEPTH_FROM_24BIT_TO_16BIT = FLAG_BIT(21), GPU_ROUND_FRAGMENT_DEPTH_TO_16BIT = FLAG_BIT(22), diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 703ada14b4..2c50795472 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -89,8 +89,6 @@ DrawEngineVulkan::DrawEngineVulkan(Draw::DrawContext *draw) decIndex = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); indexGen.Setup(decIndex); - - InitDeviceObjects(); } void DrawEngineVulkan::InitDeviceObjects() { @@ -114,6 +112,8 @@ void DrawEngineVulkan::InitDeviceObjects() { bindings[3].descriptorCount = 1; bindings[3].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; bindings[3].stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; + if (gstate_c.Supports(GPU_SUPPORTS_GS_CULLING)) + bindings[3].stageFlags |= VK_SHADER_STAGE_GEOMETRY_BIT; bindings[3].binding = DRAW_BINDING_DYNUBO_BASE; bindings[4].descriptorCount = 1; bindings[4].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; @@ -581,6 +581,7 @@ void DrawEngineVulkan::DoFlush() { VulkanVertexShader *vshader = nullptr; VulkanFragmentShader *fshader = nullptr; + VulkanGeometryShader *gshader = nullptr; uint32_t ibOffset; uint32_t vbOffset; @@ -775,14 +776,14 @@ void DrawEngineVulkan::DoFlush() { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, &gshader, pipelineState_, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform if (!vshader) { // We're screwed. return; } _dbg_assert_msg_(vshader->UseHWTransform(), "Bad vshader"); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, true, 0); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, gshader, true, 0); if (!pipeline || !pipeline->pipeline) { // Already logged, let's bail out. return; @@ -905,9 +906,9 @@ void DrawEngineVulkan::DoFlush() { if (prim != lastPrim_ || gstate_c.IsDirty(DIRTY_BLEND_STATE | DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_RASTER_STATE | DIRTY_DEPTHSTENCIL_STATE)) { ConvertStateToVulkanKey(*framebufferManager_, shaderManager_, prim, pipelineKey_, dynState_); } - shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform + shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, &gshader, pipelineState_, false, false, decOptions_.expandAllWeightsToFloat); // usehwtransform _dbg_assert_msg_(!vshader->UseHWTransform(), "Bad vshader"); - VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, false, 0); + VulkanPipeline *pipeline = pipelineManager_->GetOrCreatePipeline(renderManager, pipelineLayout_, pipelineKey_, &dec_->decFmt, vshader, fshader, gshader, false, 0); if (!pipeline || !pipeline->pipeline) { // Already logged, let's bail out. decodedVerts_ = 0; diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 50df67b136..ebf50e33b5 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -127,6 +127,9 @@ public: DrawEngineVulkan(Draw::DrawContext *draw); virtual ~DrawEngineVulkan(); + // We reference feature flags, so this is called after construction. + void InitDeviceObjects(); + void SetShaderManager(ShaderManagerVulkan *shaderManager) { shaderManager_ = shaderManager; } @@ -196,7 +199,6 @@ private: void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); void BindShaderBlendTex(); - void InitDeviceObjects(); void DestroyDeviceObjects(); void DecodeVertsToPushBuffer(VulkanPushBuffer *push, uint32_t *bindOffset, VkBuffer *vkbuf); diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index 5bf6b0a2ac..aa31c93be4 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -53,6 +53,7 @@ GPU_Vulkan::GPU_Vulkan(GraphicsContext *gfxCtx, Draw::DrawContext *draw) : GPUCommon(gfxCtx, draw), drawEngine_(draw) { gstate_c.featureFlags = CheckGPUFeatures(); + drawEngine_.InitDeviceObjects(); VulkanContext *vulkan = (VulkanContext *)gfxCtx->GetAPIContext(); diff --git a/GPU/Vulkan/PipelineManagerVulkan.cpp b/GPU/Vulkan/PipelineManagerVulkan.cpp index 2ea530d25a..f25b0cb269 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.cpp +++ b/GPU/Vulkan/PipelineManagerVulkan.cpp @@ -171,7 +171,7 @@ static std::string CutFromMain(std::string str) { static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, VkPipelineCache pipelineCache, VkPipelineLayout layout, PipelineFlags pipelineFlags, const VulkanPipelineRasterStateKey &key, - const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) { + const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantBitmask) { VulkanPipeline *vulkanPipeline = new VulkanPipeline(); VKRGraphicsPipelineDesc *desc = &vulkanPipeline->desc; desc->pipelineCache = pipelineCache; @@ -254,6 +254,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, desc->fragmentShader = fs->GetModule(); desc->vertexShader = vs->GetModule(); + desc->geometryShader = gs ? gs->GetModule() : nullptr; VkPipelineInputAssemblyStateCreateInfo &inputAssembly = desc->inputAssembly; inputAssembly.flags = 0; @@ -301,6 +302,9 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, if (useBlendConstant) { pipelineFlags |= PipelineFlags::USES_BLEND_CONSTANT; } + if (gs) { + pipelineFlags |= PipelineFlags::USES_GEOMETRY_SHADER; + } if (dss.depthTestEnable || dss.stencilTestEnable) { pipelineFlags |= PipelineFlags::USES_DEPTH_STENCIL; } @@ -308,7 +312,7 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager, return vulkanPipeline; } -VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantBitmask) { +VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantBitmask) { if (!pipelineCache_) { VkPipelineCacheCreateInfo pc{ VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; VkResult res = vkCreatePipelineCache(vulkan_->GetDevice(), &pc, nullptr, &pipelineCache_); @@ -321,6 +325,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * key.useHWTransform = useHwTransform; key.vShader = vs->GetModule(); key.fShader = fs->GetModule(); + key.gShader = gs ? gs->GetModule() : VK_NULL_HANDLE; key.vtxFmtId = useHwTransform ? decFmt->id : 0; auto iter = pipelines_.Get(key); @@ -334,7 +339,7 @@ VulkanPipeline *PipelineManagerVulkan::GetOrCreatePipeline(VulkanRenderManager * VulkanPipeline *pipeline = CreateVulkanPipeline( renderManager, pipelineCache_, layout, pipelineFlags, - rasterKey, decFmt, vs, fs, useHwTransform, variantBitmask); + rasterKey, decFmt, vs, fs, gs, useHwTransform, variantBitmask); pipelines_.Insert(key, pipeline); // Don't return placeholder null pipelines. @@ -589,7 +594,13 @@ void PipelineManagerVulkan::SaveCache(FILE *file, bool saveRawPipelineCache, Sha return; VulkanVertexShader *vshader = shaderManager->GetVertexShaderFromModule(pkey.vShader->BlockUntilReady()); VulkanFragmentShader *fshader = shaderManager->GetFragmentShaderFromModule(pkey.fShader->BlockUntilReady()); - if (!vshader || !fshader) { + VulkanGeometryShader *gshader = nullptr; + if (pkey.gShader) { + gshader = shaderManager->GetGeometryShaderFromModule(pkey.gShader->BlockUntilReady()); + if (!gshader) + failed = true; + } + if (!vshader || !fshader || failed) { failed = true; return; } @@ -710,7 +721,7 @@ bool PipelineManagerVulkan::LoadCache(FILE *file, bool loadRawPipelineCache, Sha DecVtxFormat fmt; fmt.InitializeFromID(key.vtxFmtId); - VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, key.useHWTransform, key.variants); + VulkanPipeline *pipeline = GetOrCreatePipeline(rm, layout, key.raster, key.useHWTransform ? &fmt : 0, vs, fs, nullptr, key.useHWTransform, key.variants); if (!pipeline) { pipelineCreateFailCount += 1; } diff --git a/GPU/Vulkan/PipelineManagerVulkan.h b/GPU/Vulkan/PipelineManagerVulkan.h index e72f53a800..32e45c3faf 100644 --- a/GPU/Vulkan/PipelineManagerVulkan.h +++ b/GPU/Vulkan/PipelineManagerVulkan.h @@ -38,6 +38,7 @@ struct VulkanPipelineKey { VKRRenderPass *renderPass; Promise *vShader; Promise *fShader; + Promise *gShader; uint32_t vtxFmtId; bool useHWTransform; @@ -68,6 +69,7 @@ struct VulkanPipeline { class VulkanContext; class VulkanVertexShader; class VulkanFragmentShader; +class VulkanGeometryShader; class ShaderManagerVulkan; class DrawEngineCommon; @@ -77,7 +79,7 @@ public: ~PipelineManagerVulkan(); // variantMask is only used when loading pipelines from cache. - VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, bool useHwTransform, u32 variantMask); + VulkanPipeline *GetOrCreatePipeline(VulkanRenderManager *renderManager, VkPipelineLayout layout, const VulkanPipelineRasterStateKey &rasterKey, const DecVtxFormat *decFmt, VulkanVertexShader *vs, VulkanFragmentShader *fs, VulkanGeometryShader *gs, bool useHwTransform, u32 variantMask); int GetNumPipelines() const { return (int)pipelines_.size(); } void Clear(); diff --git a/GPU/Vulkan/ShaderManagerVulkan.cpp b/GPU/Vulkan/ShaderManagerVulkan.cpp index bfb5547b6a..769c258993 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.cpp +++ b/GPU/Vulkan/ShaderManagerVulkan.cpp @@ -163,8 +163,38 @@ std::string VulkanVertexShader::GetShaderString(DebugShaderStringType type) cons } } +VulkanGeometryShader::VulkanGeometryShader(VulkanContext *vulkan, GShaderID id, const char *code) + : vulkan_(vulkan), id_(id) { + source_ = code; + module_ = CompileShaderModuleAsync(vulkan, VK_SHADER_STAGE_GEOMETRY_BIT, source_.c_str(), new std::string(GeometryShaderDesc(id).c_str())); + if (!module_) { + failed_ = true; + } else { + VERBOSE_LOG(G3D, "Compiled geometry shader:\n%s\n", (const char *)code); + } +} + +VulkanGeometryShader::~VulkanGeometryShader() { + if (module_) { + VkShaderModule shaderModule = module_->BlockUntilReady(); + vulkan_->Delete().QueueDeleteShaderModule(shaderModule); + delete module_; + } +} + +std::string VulkanGeometryShader::GetShaderString(DebugShaderStringType type) const { + switch (type) { + case SHADER_STRING_SOURCE_CODE: + return source_; + case SHADER_STRING_SHORT_DESC: + return GeometryShaderDesc(id_); + default: + return "N/A"; + } +} + ShaderManagerVulkan::ShaderManagerVulkan(Draw::DrawContext *draw) - : ShaderManagerCommon(draw), compat_(GLSL_VULKAN), fsCache_(16), vsCache_(16) { + : ShaderManagerCommon(draw), compat_(GLSL_VULKAN), fsCache_(16), vsCache_(16), gsCache_(16) { codeBuffer_ = new char[16384]; VulkanContext *vulkan = (VulkanContext *)draw->GetNativeObject(Draw::NativeObject::CONTEXT); uboAlignment_ = vulkan->GetPhysicalDeviceProperties().properties.limits.minUniformBufferOffsetAlignment; @@ -199,10 +229,15 @@ void ShaderManagerVulkan::Clear() { vsCache_.Iterate([&](const VShaderID &key, VulkanVertexShader *shader) { delete shader; }); + gsCache_.Iterate([&](const GShaderID &key, VulkanGeometryShader *shader) { + delete shader; + }); fsCache_.Clear(); vsCache_.Clear(); + gsCache_.Clear(); lastFSID_.set_invalid(); lastVSID_.set_invalid(); + lastGSID_.set_invalid(); gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); } @@ -216,12 +251,14 @@ void ShaderManagerVulkan::DirtyShader() { // Forget the last shader ID lastFSID_.set_invalid(); lastVSID_.set_invalid(); + lastGSID_.set_invalid(); DirtyLastShader(); } void ShaderManagerVulkan::DirtyLastShader() { lastVShader_ = nullptr; lastFShader_ = nullptr; + lastGShader_ = nullptr; gstate_c.Dirty(DIRTY_VERTEXSHADER_STATE | DIRTY_FRAGMENTSHADER_STATE | DIRTY_GEOMETRYSHADER_STATE); } @@ -239,7 +276,7 @@ uint64_t ShaderManagerVulkan::UpdateUniforms(bool useBufferedRendering) { return dirty; } -void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { +void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, VulkanGeometryShader **gshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat) { VShaderID VSID; if (gstate_c.IsDirty(DIRTY_VERTEXSHADER_STATE)) { gstate_c.Clean(DIRTY_VERTEXSHADER_STATE); @@ -256,14 +293,23 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader FSID = lastFSID_; } + GShaderID GSID; + if (gstate_c.IsDirty(DIRTY_GEOMETRYSHADER_STATE)) { + gstate_c.Clean(DIRTY_GEOMETRYSHADER_STATE); + ComputeGeometryShaderID(&GSID, draw_->GetBugs(), prim); + } else { + GSID = lastGSID_; + } + _dbg_assert_(FSID.Bit(FS_BIT_LMODE) == VSID.Bit(VS_BIT_LMODE)); _dbg_assert_(FSID.Bit(FS_BIT_DO_TEXTURE) == VSID.Bit(VS_BIT_DO_TEXTURE)); _dbg_assert_(FSID.Bit(FS_BIT_FLATSHADE) == VSID.Bit(VS_BIT_FLATSHADE)); // Just update uniforms if this is the same shader as last time. - if (lastVShader_ != nullptr && lastFShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_) { + if (lastVShader_ != nullptr && lastFShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_ && GSID == lastGSID_) { *vshader = lastVShader_; *fshader = lastFShader_; + *gshader = lastGShader_; _dbg_assert_msg_((*vshader)->UseHWTransform() == useHWTransform, "Bad vshader was cached"); // Already all set, no need to look up in shader maps. return; @@ -281,11 +327,9 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader vs = new VulkanVertexShader(vulkan, VSID, codeBuffer_, useHWTransform); vsCache_.Insert(VSID, vs); } - lastVSID_ = VSID; VulkanFragmentShader *fs = fsCache_.Get(FSID); if (!fs) { - // uint32_t vendorID = vulkan->GetPhysicalDeviceProperties().properties.vendorID; // Fragment shader not in cache. Let's compile it. std::string genErrorString; uint64_t uniformMask = 0; // Not used @@ -296,13 +340,28 @@ void ShaderManagerVulkan::GetShaders(int prim, u32 vertType, VulkanVertexShader fsCache_.Insert(FSID, fs); } + VulkanGeometryShader *gs; + if (GSID.Bit(GS_BIT_ENABLED)) { + gs = gsCache_.Get(GSID); + if (!gs) { + // Geometry shader not in cache. Let's compile it. + // TODO + } + } else { + gs = nullptr; + } + + lastVSID_ = VSID; lastFSID_ = FSID; + lastGSID_ = GSID; lastVShader_ = vs; lastFShader_ = fs; + lastGShader_ = gs; *vshader = vs; *fshader = fs; + *gshader = gs; _dbg_assert_msg_((*vshader)->UseHWTransform() == useHWTransform, "Bad vshader was computed"); } @@ -327,6 +386,15 @@ std::vector ShaderManagerVulkan::DebugGetShaderIDs(DebugShaderType }); break; } + case SHADER_TYPE_GEOMETRY: + { + gsCache_.Iterate([&](const GShaderID &id, VulkanGeometryShader *shader) { + std::string idstr; + id.ToString(&idstr); + ids.push_back(idstr); + }); + break; + } default: break; } @@ -342,12 +410,16 @@ std::string ShaderManagerVulkan::DebugGetShaderString(std::string id, DebugShade VulkanVertexShader *vs = vsCache_.Get(VShaderID(shaderId)); return vs ? vs->GetShaderString(stringType) : ""; } - case SHADER_TYPE_FRAGMENT: { VulkanFragmentShader *fs = fsCache_.Get(FShaderID(shaderId)); return fs ? fs->GetShaderString(stringType) : ""; } + case SHADER_TYPE_GEOMETRY: + { + VulkanGeometryShader *gs = gsCache_.Get(GShaderID(shaderId)); + return gs ? gs->GetShaderString(stringType) : ""; + } default: return "N/A"; } @@ -375,6 +447,17 @@ VulkanFragmentShader *ShaderManagerVulkan::GetFragmentShaderFromModule(VkShaderM return fs; } +VulkanGeometryShader *ShaderManagerVulkan::GetGeometryShaderFromModule(VkShaderModule module) { + VulkanGeometryShader *gs = nullptr; + gsCache_.Iterate([&](const GShaderID &id, VulkanGeometryShader *shader) { + Promise *p = shader->GetModule(); + VkShaderModule m = p->BlockUntilReady(); + if (m == module) + gs = shader; + }); + return gs; +} + // Shader cache. // // We simply store the IDs of the shaders used during gameplay. On next startup of diff --git a/GPU/Vulkan/ShaderManagerVulkan.h b/GPU/Vulkan/ShaderManagerVulkan.h index 6593a366f4..4c89f91ef5 100644 --- a/GPU/Vulkan/ShaderManagerVulkan.h +++ b/GPU/Vulkan/ShaderManagerVulkan.h @@ -83,7 +83,27 @@ protected: VShaderID id_; }; -class VulkanPushBuffer; +class VulkanGeometryShader { +public: + VulkanGeometryShader(VulkanContext *vulkan, GShaderID id, const char *code); + ~VulkanGeometryShader(); + + const std::string &source() const { return source_; } + + bool Failed() const { return failed_; } + + std::string GetShaderString(DebugShaderStringType type) const; + Promise *GetModule() const { return module_; } + const GShaderID &GetID() { return id_; } + +protected: + Promise *module_ = nullptr; + + VulkanContext *vulkan_; + std::string source_; + bool failed_ = false; + GShaderID id_; +}; class ShaderManagerVulkan : public ShaderManagerCommon { public: @@ -93,19 +113,22 @@ public: void DeviceLost(); void DeviceRestore(Draw::DrawContext *draw); - void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); + void GetShaders(int prim, u32 vertType, VulkanVertexShader **vshader, VulkanFragmentShader **fshader, VulkanGeometryShader **gshader, const ComputedPipelineState &pipelineState, bool useHWTransform, bool useHWTessellation, bool weightsAsFloat); void ClearShaders(); void DirtyShader(); void DirtyLastShader() override; int GetNumVertexShaders() const { return (int)vsCache_.size(); } int GetNumFragmentShaders() const { return (int)fsCache_.size(); } + int GetNumGeometryShaders() const { return (int)gsCache_.size(); } // Used for saving/loading the cache. Don't need to be particularly fast. VulkanVertexShader *GetVertexShaderFromID(VShaderID id) { return vsCache_.Get(id); } VulkanFragmentShader *GetFragmentShaderFromID(FShaderID id) { return fsCache_.Get(id); } + VulkanGeometryShader *GetGeometryShaderFromID(GShaderID id) { return gsCache_.Get(id); } VulkanVertexShader *GetVertexShaderFromModule(VkShaderModule module); VulkanFragmentShader *GetFragmentShaderFromModule(VkShaderModule module); + VulkanGeometryShader *GetGeometryShaderFromModule(VkShaderModule module); std::vector DebugGetShaderIDs(DebugShaderType type); std::string DebugGetShaderString(std::string id, DebugShaderType type, DebugShaderStringType stringType); @@ -143,6 +166,9 @@ private: typedef DenseHashMap VSCache; VSCache vsCache_; + typedef DenseHashMap GSCache; + GSCache gsCache_; + char *codeBuffer_; uint64_t uboAlignment_; @@ -153,7 +179,9 @@ private: VulkanFragmentShader *lastFShader_ = nullptr; VulkanVertexShader *lastVShader_ = nullptr; + VulkanGeometryShader *lastGShader_ = nullptr; FShaderID lastFSID_; VShaderID lastVSID_; + GShaderID lastGSID_; }; diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index e85f1d3adc..5dea5e0921 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -1156,7 +1156,7 @@ int ShaderListScreen::ListShaders(DebugShaderType shaderType, UI::LinearLayout * struct { DebugShaderType type; const char *name; } shaderTypes[] = { { SHADER_TYPE_VERTEX, "Vertex" }, { SHADER_TYPE_FRAGMENT, "Fragment" }, - // { SHADER_TYPE_GEOMETRY, "Geometry" }, + { SHADER_TYPE_GEOMETRY, "Geometry" }, { SHADER_TYPE_VERTEXLOADER, "VertexLoader" }, { SHADER_TYPE_PIPELINE, "Pipeline" }, { SHADER_TYPE_TEXTURE, "Texture" },