Add support for using the correct pipeline for each surface

This commit is contained in:
Magnus Norddahl 2023-10-30 01:17:51 +01:00
commit 04493ade52
10 changed files with 247 additions and 118 deletions

View file

@ -38,6 +38,7 @@
#include "hw_cvars.h"
#include "hw_skydome.h"
#include "flatvertices.h"
#include "hw_meshbuilder.h"
#include "vk_renderdevice.h"
#include "vulkan/vk_renderstate.h"
@ -70,6 +71,7 @@ FString JitCaptureStackTrace(int framesToSkip, bool includeNativeFrames, int max
EXTERN_CVAR(Int, gl_tonemap)
EXTERN_CVAR(Int, screenblocks)
EXTERN_CVAR(Bool, cl_capfps)
EXTERN_CVAR(Bool, r_skipmats)
// Physical device info
static std::vector<VulkanCompatibleDevice> SupportedDevices;
@ -611,6 +613,120 @@ int VulkanRenderDevice::GetBindlessTextureIndex(FMaterial* material, int clampmo
return static_cast<VkMaterial*>(material)->GetBindlessIndex(materialState);
}
int VulkanRenderDevice::GetLevelMeshPipelineID(const MeshApplyData& applyData, const SurfaceUniforms& surfaceUniforms, const FMaterialState& material)
{
if (levelVertexFormatIndex == -1)
{
static const std::vector<FVertexBufferAttribute> format =
{
{ 0, VATTR_VERTEX, VFmt_Float4, (int)myoffsetof(FFlatVertex, x) },
{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(FFlatVertex, u) },
{ 0, VATTR_LIGHTMAP, VFmt_Float2, (int)myoffsetof(FFlatVertex, lu) },
{ 1, VATTR_UNIFORM_INDEXES, VFmt_Int, 0 }
};
levelVertexFormatIndex = GetRenderPassManager()->GetVertexFormat({ sizeof(FFlatVertex), sizeof(int32_t) }, format);
}
VkPipelineKey pipelineKey;
pipelineKey.DrawType = DT_Triangles;
pipelineKey.VertexFormat = levelVertexFormatIndex;
pipelineKey.RenderStyle = applyData.RenderStyle;
pipelineKey.DepthTest = true; // mDepthTest;
pipelineKey.DepthWrite = true; // mDepthTest && mDepthWrite;
pipelineKey.DepthFunc = applyData.DepthFunc;
pipelineKey.DepthClamp = false; // mDepthClamp;
pipelineKey.DepthBias = false; // !(mBias.mFactor == 0 && mBias.mUnits == 0);
pipelineKey.StencilTest = false; // mStencilTest;
pipelineKey.StencilPassOp = 0; // mStencilOp;
pipelineKey.ColorMask = 15; // mColorMask;
pipelineKey.CullMode = 0; // mCullMode;
pipelineKey.NumTextureLayers = material.mMaterial ? material.mMaterial->NumLayers() : 0;
pipelineKey.NumTextureLayers = max(pipelineKey.NumTextureLayers, SHADER_MIN_REQUIRED_TEXTURE_LAYERS);// Always force minimum 8 textures as the shader requires it
if (applyData.SpecialEffect > EFF_NONE)
{
pipelineKey.ShaderKey.SpecialEffect = applyData.SpecialEffect;
pipelineKey.ShaderKey.EffectState = 0;
pipelineKey.ShaderKey.AlphaTest = false;
}
else
{
int effectState = material.mOverrideShader >= 0 ? material.mOverrideShader : (material.mMaterial ? material.mMaterial->GetShaderIndex() : 0);
pipelineKey.ShaderKey.SpecialEffect = EFF_NONE;
pipelineKey.ShaderKey.EffectState = applyData.TextureEnabled ? effectState : SHADER_NoTexture;
if (r_skipmats && pipelineKey.ShaderKey.EffectState >= 3 && pipelineKey.ShaderKey.EffectState <= 4)
pipelineKey.ShaderKey.EffectState = 0;
pipelineKey.ShaderKey.AlphaTest = surfaceUniforms.uAlphaThreshold >= 0.f;
}
int tempTM = (material.mMaterial && material.mMaterial->Source()->isHardwareCanvas()) ? TM_OPAQUE : TM_NORMAL;
int f = applyData.TextureModeFlags;
if (!applyData.BrightmapEnabled) f &= ~(TEXF_Brightmap | TEXF_Glowmap);
if (applyData.TextureClamp) f |= TEXF_ClampY;
int uTextureMode = (applyData.TextureMode == TM_NORMAL && tempTM == TM_OPAQUE ? TM_OPAQUE : applyData.TextureMode) | f;
pipelineKey.ShaderKey.TextureMode = uTextureMode & 0xffff;
pipelineKey.ShaderKey.ClampY = (uTextureMode & TEXF_ClampY) != 0;
pipelineKey.ShaderKey.Brightmap = (uTextureMode & TEXF_Brightmap) != 0;
pipelineKey.ShaderKey.Detailmap = (uTextureMode & TEXF_Detailmap) != 0;
pipelineKey.ShaderKey.Glowmap = (uTextureMode & TEXF_Glowmap) != 0;
// The way GZDoom handles state is just plain insanity!
int fogset = 0;
if (applyData.FogEnabled)
{
if (applyData.FogEnabled == 2)
{
fogset = -3; // 2D rendering with 'foggy' overlay.
}
else if (applyData.FogColor)
{
fogset = gl_fogmode;
}
else
{
fogset = -gl_fogmode;
}
}
pipelineKey.ShaderKey.Simple2D = (fogset == -3);
pipelineKey.ShaderKey.FogBeforeLights = (fogset > 0);
pipelineKey.ShaderKey.FogAfterLights = (fogset < 0);
pipelineKey.ShaderKey.FogRadial = (fogset < -1 || fogset > 1);
pipelineKey.ShaderKey.SWLightRadial = (gl_fogmode == 2);
pipelineKey.ShaderKey.SWLightBanded = false; // gl_bandedswlight;
float lightlevel = surfaceUniforms.uLightLevel;
if (lightlevel < 0.0)
{
pipelineKey.ShaderKey.LightMode = 0; // Default
}
else
{
/*if (mLightMode == 5)
pipelineKey.ShaderKey.LightMode = 3; // Build
else if (mLightMode == 16)
pipelineKey.ShaderKey.LightMode = 2; // Vanilla
else*/
pipelineKey.ShaderKey.LightMode = 1; // Software
}
pipelineKey.ShaderKey.UseShadowmap = gl_light_shadows == 1;
pipelineKey.ShaderKey.UseRaytrace = gl_light_shadows == 2;
pipelineKey.ShaderKey.GBufferPass = false;
pipelineKey.ShaderKey.UseLevelMesh = true;
for (unsigned int i = 0, count = levelMeshPipelineKeys.Size(); i < count; i++)
{
if (levelMeshPipelineKeys[i] == pipelineKey)
{
return i;
}
}
levelMeshPipelineKeys.Push(pipelineKey);
return levelMeshPipelineKeys.Size() - 1;
}
void VulkanRenderDevice::DrawLevelMesh(const HWViewpointUniforms& viewpoint)
{
auto cmdbuffer = GetCommands()->GetDrawCommands();
@ -658,48 +774,11 @@ void VulkanRenderDevice::DrawLevelMesh(const HWViewpointUniforms& viewpoint)
cmdbuffer->setStencilReference(VK_STENCIL_FRONT_AND_BACK, 0);
cmdbuffer->setDepthBias(0.0f, 0.0f, 0.0f);
static const std::vector<FVertexBufferAttribute> format =
{
{ 0, VATTR_VERTEX, VFmt_Float4, (int)myoffsetof(FFlatVertex, x) },
{ 0, VATTR_TEXCOORD, VFmt_Float2, (int)myoffsetof(FFlatVertex, u) },
{ 0, VATTR_LIGHTMAP, VFmt_Float2, (int)myoffsetof(FFlatVertex, lu) },
{ 1, VATTR_UNIFORM_INDEXES, VFmt_Int, 0 }
};
int vertexFormatIndex = GetRenderPassManager()->GetVertexFormat({ sizeof(FFlatVertex), sizeof(int32_t) }, format);
VkBuffer vertexBuffers[2] = { GetRaytrace()->GetVertexBuffer()->buffer, GetRaytrace()->GetUniformIndexBuffer()->buffer };
VkDeviceSize vertexBufferOffsets[] = { 0, 0 };
cmdbuffer->bindVertexBuffers(0, 2, vertexBuffers, vertexBufferOffsets);
cmdbuffer->bindIndexBuffer(GetRaytrace()->GetIndexBuffer()->buffer, 0, VK_INDEX_TYPE_UINT32);
VkPipelineKey pipelineKey;
pipelineKey.DrawType = DT_Triangles;
pipelineKey.VertexFormat = vertexFormatIndex;
pipelineKey.RenderStyle = DefaultRenderStyle();
pipelineKey.DepthTest = true;
pipelineKey.DepthWrite = true;
pipelineKey.DepthFunc = DF_Less;
pipelineKey.DepthClamp = false;
pipelineKey.DepthBias = false;
pipelineKey.StencilTest = false;
pipelineKey.StencilPassOp = 0;
pipelineKey.ColorMask = 15;
pipelineKey.CullMode = 0;
pipelineKey.NumTextureLayers = 0;
pipelineKey.NumTextureLayers = max(pipelineKey.NumTextureLayers, SHADER_MIN_REQUIRED_TEXTURE_LAYERS);// Always force minimum 8 textures as the shader requires it
pipelineKey.ShaderKey.SpecialEffect = EFF_NONE;
pipelineKey.ShaderKey.EffectState = SHADER_Default;
pipelineKey.ShaderKey.AlphaTest = false;
pipelineKey.ShaderKey.SWLightRadial = true;
pipelineKey.ShaderKey.LightMode = 1; // Software
pipelineKey.ShaderKey.UseShadowmap = gl_light_shadows == 1;
pipelineKey.ShaderKey.UseRaytrace = gl_light_shadows == 2;
pipelineKey.ShaderKey.GBufferPass = key.DrawBuffers > 1;
pipelineKey.ShaderKey.UseLevelMesh = true;
VulkanPipelineLayout* layout = GetRenderPassManager()->GetPipelineLayout(pipelineKey.NumTextureLayers, pipelineKey.ShaderKey.UseLevelMesh);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, passSetup->GetPipeline(pipelineKey));
auto rsbuffers = GetBufferManager()->GetRSBuffers();
memcpy(((char*)rsbuffers->Viewpoint.Data) + rsbuffers->Viewpoint.UploadIndex * rsbuffers->Viewpoint.BlockAlign, &viewpoint, sizeof(HWViewpointUniforms));
int viewpointIndex = rsbuffers->Viewpoint.UploadIndex++;
@ -710,21 +789,29 @@ void VulkanRenderDevice::DrawLevelMesh(const HWViewpointUniforms& viewpoint)
matrices.TextureMatrix.loadIdentity();
rsbuffers->MatrixBuffer->Write(matrices);
uint32_t viewpointOffset = viewpointIndex * rsbuffers->Viewpoint.BlockAlign;
uint32_t matrixOffset = rsbuffers->MatrixBuffer->Offset();
uint32_t lightsOffset = 0;
uint32_t offsets[] = { viewpointOffset, matrixOffset, lightsOffset };
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptors->GetFixedSet());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 1, descriptors->GetLevelMeshSet(), 3, offsets);
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 2, descriptors->GetBindlessSet());
auto submesh = GetRaytrace()->GetMesh()->StaticMesh.get();
for (LevelSubmeshDrawRange& range : submesh->DrawList)
{
auto& pipelineKey = levelMeshPipelineKeys[range.PipelineID];
VulkanPipelineLayout* layout = GetRenderPassManager()->GetPipelineLayout(pipelineKey.NumTextureLayers, pipelineKey.ShaderKey.UseLevelMesh);
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_GRAPHICS, passSetup->GetPipeline(pipelineKey));
PushConstants pushConstants = {};
pushConstants.uDataIndex = 0;
pushConstants.uLightIndex = -1;
pushConstants.uBoneIndexBase = -1;
cmdbuffer->pushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, (uint32_t)sizeof(PushConstants), &pushConstants);
uint32_t viewpointOffset = viewpointIndex * rsbuffers->Viewpoint.BlockAlign;
uint32_t matrixOffset = rsbuffers->MatrixBuffer->Offset();
uint32_t lightsOffset = 0;
uint32_t offsets[] = { viewpointOffset, matrixOffset, lightsOffset };
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 0, descriptors->GetFixedSet());
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 1, descriptors->GetLevelMeshSet(), 3, offsets);
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_GRAPHICS, layout, 2, descriptors->GetBindlessSet());
cmdbuffer->drawIndexed(GetRaytrace()->GetIndexCount(), 1, 0, 0, 0);
PushConstants pushConstants = {};
pushConstants.uDataIndex = 0;
pushConstants.uLightIndex = -1;
pushConstants.uBoneIndexBase = -1;
cmdbuffer->pushConstants(layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, (uint32_t)sizeof(PushConstants), &pushConstants);
cmdbuffer->drawIndexed(range.Count, 1, range.Start, 0, 0);
}
cmdbuffer->endRenderPass();
}