0
0
Fork 0
mirror of https://github.com/id-Software/DOOM-3-BFG.git synced 2025-03-17 08:00:49 +00:00
doom3-bfg/neo/renderer/Vulkan/RenderProgs_VK.cpp
Alejandro Piñeiro a0f107ec4a Make depthBounds optional for the Vulkan renderer
This is already the case for the OpenGL renderer. This feature is not
supported by all the Vulkan drivers. For example Mesa ANV with
Coffe-lake (that is relatively recent) or Mesa v3dv (Broadcom) used on
the rpi4.
2021-02-09 12:55:29 +01:00

1605 lines
48 KiB
C++

/*
===========================================================================
Doom 3 BFG Edition GPL Source Code
Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company.
Copyright (C) 2013-2018 Robert Beckebans
Copyright (C) 2016-2017 Dustin Land
This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").
Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Doom 3 BFG Edition Source Code. If not, see <http://www.gnu.org/licenses/>.
In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code. If not, please request a copy in writing from id Software at the address below.
If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
===========================================================================
*/
#pragma hdrstop
#include "precompiled.h"
#include "../RenderCommon.h"
#include "../RenderProgs.h"
void RpPrintState( uint64 stateBits );
struct vertexLayout_t
{
VkPipelineVertexInputStateCreateInfo inputState;
idList< VkVertexInputBindingDescription > bindingDesc;
idList< VkVertexInputAttributeDescription > attributeDesc;
};
idUniformBuffer emptyUBO;
static vertexLayout_t vertexLayouts[ NUM_VERTEX_LAYOUTS ];
static const char* renderProgBindingStrings[ BINDING_TYPE_MAX ] =
{
"ubo",
"sampler"
};
/*
=============
CreateVertexDescriptions
=============
*/
void CreateVertexDescriptions()
{
VkPipelineVertexInputStateCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
createInfo.pNext = NULL;
createInfo.flags = 0;
VkVertexInputBindingDescription binding = {};
VkVertexInputAttributeDescription attribute = {};
{
vertexLayout_t& layout = vertexLayouts[ LAYOUT_DRAW_VERT ];
layout.inputState = createInfo;
uint32 locationNo = 0;
uint32 offset = 0;
binding.stride = sizeof( idDrawVert );
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
layout.bindingDesc.Append( binding );
// Position
attribute.format = VK_FORMAT_R32G32B32_SFLOAT;
attribute.location = locationNo++;
attribute.offset = offset;
layout.attributeDesc.Append( attribute );
offset += sizeof( idDrawVert::xyz );
// TexCoord
attribute.format = VK_FORMAT_R16G16_SFLOAT;
attribute.location = locationNo++;
attribute.offset = offset;
layout.attributeDesc.Append( attribute );
offset += sizeof( idDrawVert::st );
// Normal
attribute.format = VK_FORMAT_R8G8B8A8_UNORM;
attribute.location = locationNo++;
attribute.offset = offset;
layout.attributeDesc.Append( attribute );
offset += sizeof( idDrawVert::normal );
// Tangent
attribute.format = VK_FORMAT_R8G8B8A8_UNORM;
attribute.location = locationNo++;
attribute.offset = offset;
layout.attributeDesc.Append( attribute );
offset += sizeof( idDrawVert::tangent );
// Color1
attribute.format = VK_FORMAT_R8G8B8A8_UNORM;
attribute.location = locationNo++;
attribute.offset = offset;
layout.attributeDesc.Append( attribute );
offset += sizeof( idDrawVert::color );
// Color2
attribute.format = VK_FORMAT_R8G8B8A8_UNORM;
attribute.location = locationNo++;
attribute.offset = offset;
layout.attributeDesc.Append( attribute );
}
{
vertexLayout_t& layout = vertexLayouts[ LAYOUT_DRAW_SHADOW_VERT_SKINNED ];
layout.inputState = createInfo;
uint32 locationNo = 0;
uint32 offset = 0;
binding.stride = sizeof( idShadowVertSkinned );
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
layout.bindingDesc.Append( binding );
// Position
attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT;
attribute.location = locationNo++;
attribute.offset = offset;
layout.attributeDesc.Append( attribute );
offset += sizeof( idShadowVertSkinned::xyzw );
// Color1
attribute.format = VK_FORMAT_R8G8B8A8_UNORM;
attribute.location = locationNo++;
attribute.offset = offset;
layout.attributeDesc.Append( attribute );
offset += sizeof( idShadowVertSkinned::color );
// Color2
attribute.format = VK_FORMAT_R8G8B8A8_UNORM;
attribute.location = locationNo++;
attribute.offset = offset;
layout.attributeDesc.Append( attribute );
}
{
vertexLayout_t& layout = vertexLayouts[ LAYOUT_DRAW_SHADOW_VERT ];
layout.inputState = createInfo;
binding.stride = sizeof( idShadowVert );
binding.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
layout.bindingDesc.Append( binding );
// Position
attribute.format = VK_FORMAT_R32G32B32A32_SFLOAT;
attribute.location = 0;
attribute.offset = 0;
layout.attributeDesc.Append( attribute );
}
}
/*
========================
CreateDescriptorPools
========================
*/
void CreateDescriptorPools( VkDescriptorPool( &pools )[ NUM_FRAME_DATA ] )
{
const int numPools = 2;
VkDescriptorPoolSize poolSizes[ numPools ];
poolSizes[ 0 ].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
poolSizes[ 0 ].descriptorCount = MAX_DESC_UNIFORM_BUFFERS;
poolSizes[ 1 ].type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
poolSizes[ 1 ].descriptorCount = MAX_DESC_IMAGE_SAMPLERS;
VkDescriptorPoolCreateInfo poolCreateInfo = {};
poolCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
poolCreateInfo.pNext = NULL;
poolCreateInfo.maxSets = MAX_DESC_SETS;
poolCreateInfo.poolSizeCount = numPools;
poolCreateInfo.pPoolSizes = poolSizes;
for( int i = 0; i < NUM_FRAME_DATA; ++i )
{
ID_VK_CHECK( vkCreateDescriptorPool( vkcontext.device, &poolCreateInfo, NULL, &pools[ i ] ) );
}
}
/*
========================
GetDescriptorType
========================
*/
static VkDescriptorType GetDescriptorType( rpBinding_t type )
{
switch( type )
{
case BINDING_TYPE_UNIFORM_BUFFER:
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
case BINDING_TYPE_SAMPLER:
return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
default:
idLib::Error( "Unknown rpBinding_t %d", static_cast< int >( type ) );
return VK_DESCRIPTOR_TYPE_MAX_ENUM;
}
}
/*
========================
CreateDescriptorSetLayout
========================
*/
void idRenderProgManager::CreateDescriptorSetLayout( const shader_t& vertexShader, const shader_t& fragmentShader, renderProg_t& renderProg )
{
// Descriptor Set Layout
{
idList< VkDescriptorSetLayoutBinding > layoutBindings;
VkDescriptorSetLayoutBinding binding = {};
binding.descriptorCount = 1;
uint32 bindingId = 0;
binding.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
for( int i = 0; i < vertexShader.bindings.Num(); ++i )
{
binding.binding = bindingId++;
binding.descriptorType = GetDescriptorType( vertexShader.bindings[ i ] );
renderProg.bindings.Append( vertexShader.bindings[ i ] );
layoutBindings.Append( binding );
}
binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;
for( int i = 0; i < fragmentShader.bindings.Num(); ++i )
{
binding.binding = bindingId++;
binding.descriptorType = GetDescriptorType( fragmentShader.bindings[ i ] );
renderProg.bindings.Append( fragmentShader.bindings[ i ] );
layoutBindings.Append( binding );
}
VkDescriptorSetLayoutCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
createInfo.bindingCount = layoutBindings.Num();
createInfo.pBindings = layoutBindings.Ptr();
vkCreateDescriptorSetLayout( vkcontext.device, &createInfo, NULL, &renderProg.descriptorSetLayout );
}
// Pipeline Layout
{
VkPipelineLayoutCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
createInfo.setLayoutCount = 1;
createInfo.pSetLayouts = &renderProg.descriptorSetLayout;
vkCreatePipelineLayout( vkcontext.device, &createInfo, NULL, &renderProg.pipelineLayout );
}
}
/*
========================
idRenderProgManager::StartFrame
========================
*/
void idRenderProgManager::StartFrame()
{
counter++;
currentData = counter % NUM_FRAME_DATA;
currentDescSet = 0;
currentParmBufferOffset = 0;
vkResetDescriptorPool( vkcontext.device, descriptorPools[ currentData ], 0 );
}
/*
================================================================================================
idRenderProgManager::BindProgram
================================================================================================
*/
void idRenderProgManager::BindProgram( int index )
{
if( current == index )
{
return;
}
current = index;
RENDERLOG_PRINTF( "Binding SPIR-V Program %s\n", renderProgs[ index ].name.c_str() );
}
/*
================================================================================================
idRenderProgManager::Unbind
================================================================================================
*/
void idRenderProgManager::Unbind()
{
current = -1;
}
/*
================================================================================================
idRenderProgManager::LoadShader
================================================================================================
*/
void idRenderProgManager::LoadShader( int index, rpStage_t stage )
{
if( shaders[ index ].module != VK_NULL_HANDLE )
{
return; // Already loaded
}
LoadShader( shaders[index] );
}
/*
================================================================================================
CompileGLSLtoSPIRV
================================================================================================
*/
#if defined(SPIRV_SHADERC)
#include <shaderc/shaderc.hpp>
static int CompileGLSLtoSPIRV( const char* filename, const idStr& dataGLSL, const rpStage_t stage, uint32** spirvBuffer )
{
shaderc::Compiler compiler;
shaderc::CompileOptions options;
// Like -DMY_DEFINE=1
//options.AddMacroDefinition("MY_DEFINE", "1");
//if (optimize)
{
options.SetOptimizationLevel( shaderc_optimization_level_size );
}
shaderc_shader_kind shaderKind;
if( stage == SHADER_STAGE_VERTEX )
{
shaderKind = shaderc_glsl_vertex_shader;
}
else if( stage == SHADER_STAGE_COMPUTE )
{
shaderKind = shaderc_glsl_compute_shader;
}
else
{
shaderKind = shaderc_glsl_fragment_shader;
}
std::string source = dataGLSL.c_str();
shaderc::SpvCompilationResult module = compiler.CompileGlslToSpv( source, shaderKind, filename, options );
if( module.GetCompilationStatus() != shaderc_compilation_status_success )
{
idLib::Printf( "Comping GLSL to SPIR-V using shaderc failed for: %s\n", filename );
idLib::Printf( "%s\n", module.GetErrorMessage().c_str() );
return 0;
}
std::vector<uint32_t> spirV = { module.cbegin(), module.cend() };
// copy to spirvBuffer
int32 spirvLen = spirV.size() * sizeof( uint32_t );
void* buffer = Mem_Alloc( spirvLen, TAG_RENDERPROG );
memcpy( buffer, spirV.data(), spirvLen );
*spirvBuffer = ( uint32_t* ) buffer;
return spirvLen;
}
#else
//#include <glslang/Public/ShaderLang.h>
//#include <glslang/Include/ResourceLimits.h>
//#include <SPIRV/GlslangToSpv.h>
//#include <glslang/StandAlone/DirStackFileIncluder.h>
#include "../../extern/glslang/glslang/Public/ShaderLang.h"
#include "../../extern/glslang/glslang/Include/ResourceLimits.h"
#include "../../extern/glslang/SPIRV/GlslangToSpv.h"
static bool glslangInitialized = false;
static int CompileGLSLtoSPIRV( const char* filename, const idStr& dataGLSL, const rpStage_t stage, uint32** spirvBuffer )
{
if( !glslangInitialized )
{
glslang::InitializeProcess();
glslangInitialized = true;
}
const char* inputCString = dataGLSL.c_str();
EShLanguage shaderType;
if( stage == SHADER_STAGE_VERTEX )
{
shaderType = EShLangVertex;
}
else if( stage == SHADER_STAGE_COMPUTE )
{
shaderType = EShLangCompute;
}
else
{
shaderType = EShLangFragment;
}
glslang::TShader shader( shaderType );
shader.setStrings( &inputCString, 1 );
int clientInputSemanticsVersion = 100; // maps to, say, #define VULKAN 100
glslang::EShTargetClientVersion vulkanClientVersion = glslang::EShTargetVulkan_1_0;
glslang::EShTargetLanguageVersion targetVersion = glslang::EShTargetSpv_1_0;
shader.setEnvInput( glslang::EShSourceGlsl, shaderType, glslang::EShClientVulkan, clientInputSemanticsVersion );
shader.setEnvClient( glslang::EShClientVulkan, vulkanClientVersion );
shader.setEnvTarget( glslang::EShTargetSpv, targetVersion );
// RB: see RBDOOM-3-BFG\neo\extern\glslang\StandAlone\ResourceLimits.cpp
static TBuiltInResource resources;
resources.maxLights = 32;
resources.maxClipPlanes = 6;
resources.maxTextureUnits = 32;
resources.maxTextureCoords = 32;
resources.maxVertexAttribs = 64;
resources.maxVertexUniformComponents = 4096;
resources.maxVaryingFloats = 64;
resources.maxVertexTextureImageUnits = 32;
resources.maxCombinedTextureImageUnits = 80;
resources.maxTextureImageUnits = 32;
resources.maxFragmentUniformComponents = 4096;
resources.maxDrawBuffers = 32;
resources.maxVertexUniformVectors = 128;
resources.maxVaryingVectors = 8;
resources.maxFragmentUniformVectors = 16;
resources.maxVertexOutputVectors = 16;
resources.maxFragmentInputVectors = 15;
resources.minProgramTexelOffset = -8;
resources.maxProgramTexelOffset = 7;
resources.maxClipDistances = 8;
resources.maxComputeWorkGroupCountX = 65535;
resources.maxComputeWorkGroupCountY = 65535;
resources.maxComputeWorkGroupCountZ = 65535;
resources.maxComputeWorkGroupSizeX = 1024;
resources.maxComputeWorkGroupSizeY = 1024;
resources.maxComputeWorkGroupSizeZ = 64;
resources.maxComputeUniformComponents = 1024;
resources.maxComputeTextureImageUnits = 16;
resources.maxComputeImageUniforms = 8;
resources.maxComputeAtomicCounters = 8;
resources.maxComputeAtomicCounterBuffers = 1;
resources.maxVaryingComponents = 60;
resources.maxVertexOutputComponents = 64;
resources.maxGeometryInputComponents = 64;
resources.maxGeometryOutputComponents = 128;
resources.maxFragmentInputComponents = 128;
resources.maxImageUnits = 8;
resources.maxCombinedImageUnitsAndFragmentOutputs = 8;
resources.maxCombinedShaderOutputResources = 8;
resources.maxImageSamples = 0;
resources.maxVertexImageUniforms = 0;
resources.maxTessControlImageUniforms = 0;
resources.maxTessEvaluationImageUniforms = 0;
resources.maxGeometryImageUniforms = 0;
resources.maxFragmentImageUniforms = 8;
resources.maxCombinedImageUniforms = 8;
resources.maxGeometryTextureImageUnits = 16;
resources.maxGeometryOutputVertices = 256;
resources.maxGeometryTotalOutputComponents = 1024;
resources.maxGeometryUniformComponents = 1024;
resources.maxGeometryVaryingComponents = 64;
resources.maxTessControlInputComponents = 128;
resources.maxTessControlOutputComponents = 128;
resources.maxTessControlTextureImageUnits = 16;
resources.maxTessControlUniformComponents = 1024;
resources.maxTessControlTotalOutputComponents = 4096;
resources.maxTessEvaluationInputComponents = 128;
resources.maxTessEvaluationOutputComponents = 128;
resources.maxTessEvaluationTextureImageUnits = 16;
resources.maxTessEvaluationUniformComponents = 1024;
resources.maxTessPatchComponents = 120;
resources.maxPatchVertices = 32;
resources.maxTessGenLevel = 64;
resources.maxViewports = 16;
resources.maxVertexAtomicCounters = 0;
resources.maxTessControlAtomicCounters = 0;
resources.maxTessEvaluationAtomicCounters = 0;
resources.maxGeometryAtomicCounters = 0;
resources.maxFragmentAtomicCounters = 8;
resources.maxCombinedAtomicCounters = 8;
resources.maxAtomicCounterBindings = 1;
resources.maxVertexAtomicCounterBuffers = 0;
resources.maxTessControlAtomicCounterBuffers = 0;
resources.maxTessEvaluationAtomicCounterBuffers = 0;
resources.maxGeometryAtomicCounterBuffers = 0;
resources.maxFragmentAtomicCounterBuffers = 1;
resources.maxCombinedAtomicCounterBuffers = 1;
resources.maxAtomicCounterBufferSize = 16384;
resources.maxTransformFeedbackBuffers = 4;
resources.maxTransformFeedbackInterleavedComponents = 64;
resources.maxCullDistances = 8;
resources.maxCombinedClipAndCullDistances = 8;
resources.maxSamples = 4;
resources.maxMeshOutputVerticesNV = 256;
resources.maxMeshOutputPrimitivesNV = 512;
resources.maxMeshWorkGroupSizeX_NV = 32;
resources.maxMeshWorkGroupSizeY_NV = 1;
resources.maxMeshWorkGroupSizeZ_NV = 1;
resources.maxTaskWorkGroupSizeX_NV = 32;
resources.maxTaskWorkGroupSizeY_NV = 1;
resources.maxTaskWorkGroupSizeZ_NV = 1;
resources.maxMeshViewCountNV = 4;
resources.limits.nonInductiveForLoops = true;
resources.limits.whileLoops = true;
resources.limits.doWhileLoops = true;
resources.limits.generalUniformIndexing = true;
resources.limits.generalAttributeMatrixVectorIndexing = true;
resources.limits.generalVaryingIndexing = true;
resources.limits.generalSamplerIndexing = true;
resources.limits.generalVariableIndexing = true;
resources.limits.generalConstantMatrixVectorIndexing = true;
EShMessages messages = ( EShMessages )( EShMsgSpvRules | EShMsgVulkanRules );
const int defaultVersion = 100;
if( !shader.parse( &resources, 100, false, messages ) )
{
idLib::Printf( "GLSL parsing failed for: %s\n", filename );
idLib::Printf( "%s\n", shader.getInfoLog() );
idLib::Printf( "%s\n", shader.getInfoDebugLog() );
//*spirvBuffer = NULL;
//return 0;
}
glslang::TProgram program;
program.addShader( &shader );
if( !program.link( messages ) )
{
idLib::Printf( "GLSL linking failed for: %s\n", filename );
idLib::Printf( "%s\n", shader.getInfoLog() );
idLib::Printf( "%s\n", shader.getInfoDebugLog() );
}
// All that's left to do now is to convert the program's intermediate representation into SpirV:
std::vector<unsigned int> spirV;
spv::SpvBuildLogger logger;
glslang::SpvOptions spvOptions;
glslang::GlslangToSpv( *program.getIntermediate( shaderType ), spirV, &logger, &spvOptions );
int32 spirvLen = spirV.size() * sizeof( unsigned int );
void* buffer = Mem_Alloc( spirvLen, TAG_RENDERPROG );
memcpy( buffer, spirV.data(), spirvLen );
*spirvBuffer = ( uint32* ) buffer;
return spirvLen;
}
#endif
/*
================================================================================================
idRenderProgManager::LoadGLSLShader
================================================================================================
*/
void idRenderProgManager::LoadShader( shader_t& shader )
{
idStr inFile;
idStr outFileHLSL;
idStr outFileGLSL;
idStr outFileSPIRV;
idStr outFileLayout;
// RB: replaced backslashes
inFile.Format( "renderprogs/%s", shader.name.c_str() );
inFile.StripFileExtension();
outFileHLSL.Format( "renderprogs/hlsl/%s%s", shader.name.c_str(), shader.nameOutSuffix.c_str() );
outFileHLSL.StripFileExtension();
outFileGLSL.Format( "renderprogs/vkglsl/%s%s", shader.name.c_str(), shader.nameOutSuffix.c_str() );
outFileLayout.Format( "renderprogs/vkglsl/%s%s", shader.name.c_str(), shader.nameOutSuffix.c_str() );
outFileGLSL.StripFileExtension();
outFileLayout.StripFileExtension();
if( shader.stage == SHADER_STAGE_FRAGMENT )
{
inFile += ".ps.hlsl";
outFileHLSL += ".ps.hlsl";
outFileGLSL += ".frag";
outFileLayout += ".frag.layout";
outFileSPIRV += ".fspv";
}
else
{
inFile += ".vs.hlsl";
outFileHLSL += ".vs.hlsl";
outFileGLSL += ".vert";
outFileLayout += ".vert.layout";
outFileSPIRV += ".vspv";
}
// first check whether we already have a valid GLSL file and compare it to the hlsl timestamp;
ID_TIME_T hlslTimeStamp;
int hlslFileLength = fileSystem->ReadFile( inFile.c_str(), NULL, &hlslTimeStamp );
ID_TIME_T glslTimeStamp;
int glslFileLength = fileSystem->ReadFile( outFileGLSL.c_str(), NULL, &glslTimeStamp );
// if the glsl file doesn't exist or we have a newer HLSL file we need to recreate the glsl file.
idStr programGLSL;
idStr programLayout;
//if( ( glslFileLength <= 0 ) || ( hlslTimeStamp != FILE_NOT_FOUND_TIMESTAMP && hlslTimeStamp > glslTimeStamp ) || r_alwaysExportGLSL.GetBool() )
{
const char* hlslFileBuffer = NULL;
int len = 0;
if( hlslFileLength <= 0 )
{
hlslFileBuffer = FindEmbeddedSourceShader( inFile.c_str() );
if( hlslFileBuffer == NULL )
{
// hlsl file doesn't even exist bail out
idLib::Error( "HLSL file %s could not be loaded and may be corrupt", inFile.c_str() );
return;
}
len = strlen( hlslFileBuffer );
}
else
{
len = fileSystem->ReadFile( inFile.c_str(), ( void** ) &hlslFileBuffer );
}
if( len <= 0 )
{
return;
}
idStrList compileMacros;
for( int j = 0; j < MAX_SHADER_MACRO_NAMES; j++ )
{
if( BIT( j ) & shader.shaderFeatures )
{
const char* macroName = GetGLSLMacroName( ( shaderFeature_t ) j );
compileMacros.Append( idStr( macroName ) );
}
}
// FIXME: we should really scan the program source code for using rpEnableSkinning but at this
// point we directly load a binary and the program source code is not available on the consoles
bool hasGPUSkinning = false;
if( idStr::Icmp( shader.name.c_str(), "heatHaze" ) == 0 ||
idStr::Icmp( shader.name.c_str(), "heatHazeWithMask" ) == 0 ||
idStr::Icmp( shader.name.c_str(), "heatHazeWithMaskAndVertex" ) == 0 ||
( BIT( USE_GPU_SKINNING ) & shader.shaderFeatures ) )
{
hasGPUSkinning = true;
}
idStr hlslCode( hlslFileBuffer );
idStr programHLSL = StripDeadCode( hlslCode, inFile, compileMacros, shader.builtin );
programGLSL = ConvertCG2GLSL( programHLSL, inFile, shader.stage, programLayout, true, hasGPUSkinning, shader.vertexLayout );
fileSystem->WriteFile( outFileHLSL, programHLSL.c_str(), programHLSL.Length(), "fs_savepath" );
fileSystem->WriteFile( outFileGLSL, programGLSL.c_str(), programGLSL.Length(), "fs_savepath" );
fileSystem->WriteFile( outFileLayout, programLayout.c_str(), programLayout.Length(), "fs_savepath" );
}
/*
else
{
// read in the glsl file
void* fileBufferGLSL = NULL;
int lengthGLSL = fileSystem->ReadFile( outFileGLSL.c_str(), &fileBufferGLSL );
if( lengthGLSL <= 0 )
{
idLib::Error( "GLSL file %s could not be loaded and may be corrupt", outFileGLSL.c_str() );
}
programGLSL = ( const char* ) fileBufferGLSL;
Mem_Free( fileBufferGLSL );
{
// read in the uniform file
void* fileBufferUniforms = NULL;
int lengthUniforms = fileSystem->ReadFile( outFileUniforms.c_str(), &fileBufferUniforms );
if( lengthUniforms <= 0 )
{
idLib::Error( "uniform file %s could not be loaded and may be corrupt", outFileUniforms.c_str() );
}
programUniforms = ( const char* ) fileBufferUniforms;
Mem_Free( fileBufferUniforms );
}
}
*/
// RB: find the uniforms locations in either the vertex or fragment uniform array
// this uses the new layout structure
{
idLexer src( programLayout, programLayout.Length(), "layout" );
idToken token;
if( src.ExpectTokenString( "uniforms" ) )
{
src.ExpectTokenString( "[" );
while( !src.CheckTokenString( "]" ) )
{
src.ReadToken( &token );
int index = -1;
for( int i = 0; i < RENDERPARM_TOTAL && index == -1; i++ )
{
const char* parmName = GetGLSLParmName( i );
if( token == parmName )
{
index = i;
}
}
if( index == -1 )
{
idLib::Error( "couldn't find uniform %s for %s", token.c_str(), outFileGLSL.c_str() );
}
shader.parmIndices.Append( index );
}
}
if( src.ExpectTokenString( "bindings" ) )
{
src.ExpectTokenString( "[" );
while( !src.CheckTokenString( "]" ) )
{
src.ReadToken( &token );
int index = -1;
for( int i = 0; i < BINDING_TYPE_MAX; ++i )
{
if( token == renderProgBindingStrings[ i ] )
{
index = i;
}
}
if( index == -1 )
{
idLib::Error( "Invalid binding %s", token.c_str() );
}
shader.bindings.Append( static_cast< rpBinding_t >( index ) );
}
}
}
// TODO GLSL to SPIR-V compilation
uint32* spirvBuffer = NULL;
int spirvLen = CompileGLSLtoSPIRV( outFileGLSL.c_str(), programGLSL, shader.stage, &spirvBuffer );
VkShaderModuleCreateInfo shaderModuleCreateInfo = {};
shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
shaderModuleCreateInfo.codeSize = spirvLen;
shaderModuleCreateInfo.pCode = ( uint32* )spirvBuffer;
ID_VK_CHECK( vkCreateShaderModule( vkcontext.device, &shaderModuleCreateInfo, NULL, &shader.module ) );
Mem_Free( spirvBuffer );
}
/*
================================================================================================
idRenderProgManager::LoadGLSLProgram
================================================================================================
*/
void idRenderProgManager::LoadGLSLProgram( const int programIndex, const int vertexShaderIndex, const int fragmentShaderIndex )
{
renderProg_t& prog = renderProgs[programIndex];
if( prog.progId != INVALID_PROGID )
{
return; // Already loaded
}
idStr programName = shaders[ vertexShaderIndex ].name;
programName.StripFileExtension();
prog.name = programName;
//prog.progId = programIndex;
prog.fragmentShaderIndex = fragmentShaderIndex;
prog.vertexShaderIndex = vertexShaderIndex;
CreateDescriptorSetLayout( shaders[ vertexShaderIndex ], shaders[ fragmentShaderIndex ], prog );
// TODO
#if 1
// RB: removed idStr::Icmp( name, "heatHaze.vfp" ) == 0 hack
for( int i = 0; i < shaders[vertexShaderIndex].parmIndices.Num(); i++ )
{
if( shaders[vertexShaderIndex].parmIndices[i] == RENDERPARM_ENABLE_SKINNING )
{
prog.usesJoints = true;
prog.optionalSkinning = true;
}
}
#endif
}
/*
================================================================================================
idRenderProgManager::KillAllShaders()
================================================================================================
*/
void idRenderProgManager::KillAllShaders()
{
Unbind();
// destroy shaders
for( int i = 0; i < shaders.Num(); ++i )
{
shader_t& shader = shaders[ i ];
vkDestroyShaderModule( vkcontext.device, shader.module, NULL );
shader.module = VK_NULL_HANDLE;
}
// destroy pipelines
for( int i = 0; i < renderProgs.Num(); ++i )
{
renderProg_t& prog = renderProgs[ i ];
for( int j = 0; j < prog.pipelines.Num(); ++j )
{
vkDestroyPipeline( vkcontext.device, prog.pipelines[ j ].pipeline, NULL );
}
prog.pipelines.Clear();
vkDestroyDescriptorSetLayout( vkcontext.device, prog.descriptorSetLayout, NULL );
vkDestroyPipelineLayout( vkcontext.device, prog.pipelineLayout, NULL );
}
renderProgs.Clear();
for( int i = 0; i < NUM_FRAME_DATA; ++i )
{
parmBuffers[ i ]->FreeBufferObject();
delete parmBuffers[ i ];
parmBuffers[ i ] = NULL;
}
emptyUBO.FreeBufferObject();
for( int i = 0; i < NUM_FRAME_DATA; ++i )
{
//vkFreeDescriptorSets( vkcontext.device, descriptorPools[ i ], MAX_DESC_SETS, descriptorSets[ i ] );
vkResetDescriptorPool( vkcontext.device, descriptorPools[ i ], 0 );
vkDestroyDescriptorPool( vkcontext.device, descriptorPools[ i ], NULL );
}
memset( descriptorSets, 0, sizeof( descriptorSets ) );
memset( descriptorPools, 0, sizeof( descriptorPools ) );
counter = 0;
currentData = 0;
currentDescSet = 0;
}
/*
========================
idRenderProgManager::AllocParmBlockBuffer
========================
*/
void idRenderProgManager::AllocParmBlockBuffer( const idList<int>& parmIndices, idUniformBuffer& ubo )
{
// TODO support shadow matrices + 23 float4
const int numParms = parmIndices.Num();
const int bytes = ALIGN( numParms * sizeof( idVec4 ), vkcontext.gpu->props.limits.minUniformBufferOffsetAlignment );
ubo.Reference( *parmBuffers[ currentData ], currentParmBufferOffset, bytes );
idVec4* gpuUniforms = ( idVec4* )ubo.MapBuffer( BM_WRITE );
for( int i = 0; i < numParms; ++i )
{
gpuUniforms[ i ] = uniforms[ static_cast< renderParm_t >( parmIndices[ i ] ) ];
}
ubo.UnmapBuffer();
currentParmBufferOffset += bytes;
}
/*
========================
GetStencilOpState
========================
*/
static VkStencilOpState GetStencilOpState( uint64 stencilBits )
{
VkStencilOpState state = {};
switch( stencilBits & GLS_STENCIL_OP_FAIL_BITS )
{
case GLS_STENCIL_OP_FAIL_KEEP:
state.failOp = VK_STENCIL_OP_KEEP;
break;
case GLS_STENCIL_OP_FAIL_ZERO:
state.failOp = VK_STENCIL_OP_ZERO;
break;
case GLS_STENCIL_OP_FAIL_REPLACE:
state.failOp = VK_STENCIL_OP_REPLACE;
break;
case GLS_STENCIL_OP_FAIL_INCR:
state.failOp = VK_STENCIL_OP_INCREMENT_AND_CLAMP;
break;
case GLS_STENCIL_OP_FAIL_DECR:
state.failOp = VK_STENCIL_OP_DECREMENT_AND_CLAMP;
break;
case GLS_STENCIL_OP_FAIL_INVERT:
state.failOp = VK_STENCIL_OP_INVERT;
break;
case GLS_STENCIL_OP_FAIL_INCR_WRAP:
state.failOp = VK_STENCIL_OP_INCREMENT_AND_WRAP;
break;
case GLS_STENCIL_OP_FAIL_DECR_WRAP:
state.failOp = VK_STENCIL_OP_DECREMENT_AND_WRAP;
break;
}
switch( stencilBits & GLS_STENCIL_OP_ZFAIL_BITS )
{
case GLS_STENCIL_OP_ZFAIL_KEEP:
state.depthFailOp = VK_STENCIL_OP_KEEP;
break;
case GLS_STENCIL_OP_ZFAIL_ZERO:
state.depthFailOp = VK_STENCIL_OP_ZERO;
break;
case GLS_STENCIL_OP_ZFAIL_REPLACE:
state.depthFailOp = VK_STENCIL_OP_REPLACE;
break;
case GLS_STENCIL_OP_ZFAIL_INCR:
state.depthFailOp = VK_STENCIL_OP_INCREMENT_AND_CLAMP;
break;
case GLS_STENCIL_OP_ZFAIL_DECR:
state.depthFailOp = VK_STENCIL_OP_DECREMENT_AND_CLAMP;
break;
case GLS_STENCIL_OP_ZFAIL_INVERT:
state.depthFailOp = VK_STENCIL_OP_INVERT;
break;
case GLS_STENCIL_OP_ZFAIL_INCR_WRAP:
state.depthFailOp = VK_STENCIL_OP_INCREMENT_AND_WRAP;
break;
case GLS_STENCIL_OP_ZFAIL_DECR_WRAP:
state.depthFailOp = VK_STENCIL_OP_DECREMENT_AND_WRAP;
break;
}
switch( stencilBits & GLS_STENCIL_OP_PASS_BITS )
{
case GLS_STENCIL_OP_PASS_KEEP:
state.passOp = VK_STENCIL_OP_KEEP;
break;
case GLS_STENCIL_OP_PASS_ZERO:
state.passOp = VK_STENCIL_OP_ZERO;
break;
case GLS_STENCIL_OP_PASS_REPLACE:
state.passOp = VK_STENCIL_OP_REPLACE;
break;
case GLS_STENCIL_OP_PASS_INCR:
state.passOp = VK_STENCIL_OP_INCREMENT_AND_CLAMP;
break;
case GLS_STENCIL_OP_PASS_DECR:
state.passOp = VK_STENCIL_OP_DECREMENT_AND_CLAMP;
break;
case GLS_STENCIL_OP_PASS_INVERT:
state.passOp = VK_STENCIL_OP_INVERT;
break;
case GLS_STENCIL_OP_PASS_INCR_WRAP:
state.passOp = VK_STENCIL_OP_INCREMENT_AND_WRAP;
break;
case GLS_STENCIL_OP_PASS_DECR_WRAP:
state.passOp = VK_STENCIL_OP_DECREMENT_AND_WRAP;
break;
}
return state;
}
/*
========================
CreateGraphicsPipeline
========================
*/
static VkPipeline CreateGraphicsPipeline(
vertexLayoutType_t vertexLayoutType,
VkShaderModule vertexShader,
VkShaderModule fragmentShader,
VkPipelineLayout pipelineLayout,
uint64 stateBits )
{
// Pipeline
vertexLayout_t& vertexLayout = vertexLayouts[ vertexLayoutType ];
// Vertex Input
VkPipelineVertexInputStateCreateInfo vertexInputState = vertexLayout.inputState;
vertexInputState.vertexBindingDescriptionCount = vertexLayout.bindingDesc.Num();
vertexInputState.pVertexBindingDescriptions = vertexLayout.bindingDesc.Ptr();
vertexInputState.vertexAttributeDescriptionCount = vertexLayout.attributeDesc.Num();
vertexInputState.pVertexAttributeDescriptions = vertexLayout.attributeDesc.Ptr();
// Input Assembly
VkPipelineInputAssemblyStateCreateInfo assemblyInputState = {};
assemblyInputState.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
assemblyInputState.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
// Rasterization
VkPipelineRasterizationStateCreateInfo rasterizationState = {};
rasterizationState.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterizationState.rasterizerDiscardEnable = VK_FALSE;
rasterizationState.depthBiasEnable = ( stateBits & GLS_POLYGON_OFFSET ) != 0;
rasterizationState.depthClampEnable = VK_FALSE;
rasterizationState.frontFace = ( stateBits & GLS_CLOCKWISE ) ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterizationState.lineWidth = 1.0f;
rasterizationState.polygonMode = ( stateBits & GLS_POLYMODE_LINE ) ? VK_POLYGON_MODE_LINE : VK_POLYGON_MODE_FILL;
switch( stateBits & GLS_CULL_BITS )
{
case GLS_CULL_TWOSIDED:
rasterizationState.cullMode = VK_CULL_MODE_NONE;
break;
case GLS_CULL_BACKSIDED:
if( stateBits & GLS_MIRROR_VIEW )
{
rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT;
}
else
{
rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT;
}
break;
case GLS_CULL_FRONTSIDED:
default:
if( stateBits & GLS_MIRROR_VIEW )
{
rasterizationState.cullMode = VK_CULL_MODE_BACK_BIT;
}
else
{
rasterizationState.cullMode = VK_CULL_MODE_FRONT_BIT;
}
break;
}
// Color Blend Attachment
VkPipelineColorBlendAttachmentState attachmentState = {};
{
VkBlendFactor srcFactor = VK_BLEND_FACTOR_ONE;
switch( stateBits & GLS_SRCBLEND_BITS )
{
case GLS_SRCBLEND_ZERO:
srcFactor = VK_BLEND_FACTOR_ZERO;
break;
case GLS_SRCBLEND_ONE:
srcFactor = VK_BLEND_FACTOR_ONE;
break;
case GLS_SRCBLEND_DST_COLOR:
srcFactor = VK_BLEND_FACTOR_DST_COLOR;
break;
case GLS_SRCBLEND_ONE_MINUS_DST_COLOR:
srcFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_COLOR;
break;
case GLS_SRCBLEND_SRC_ALPHA:
srcFactor = VK_BLEND_FACTOR_SRC_ALPHA;
break;
case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA:
srcFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
break;
case GLS_SRCBLEND_DST_ALPHA:
srcFactor = VK_BLEND_FACTOR_DST_ALPHA;
break;
case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA:
srcFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
break;
}
VkBlendFactor dstFactor = VK_BLEND_FACTOR_ZERO;
switch( stateBits & GLS_DSTBLEND_BITS )
{
case GLS_DSTBLEND_ZERO:
dstFactor = VK_BLEND_FACTOR_ZERO;
break;
case GLS_DSTBLEND_ONE:
dstFactor = VK_BLEND_FACTOR_ONE;
break;
case GLS_DSTBLEND_SRC_COLOR:
dstFactor = VK_BLEND_FACTOR_SRC_COLOR;
break;
case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR:
dstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_COLOR;
break;
case GLS_DSTBLEND_SRC_ALPHA:
dstFactor = VK_BLEND_FACTOR_SRC_ALPHA;
break;
case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA:
dstFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
break;
case GLS_DSTBLEND_DST_ALPHA:
dstFactor = VK_BLEND_FACTOR_DST_ALPHA;
break;
case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA:
dstFactor = VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA;
break;
}
VkBlendOp blendOp = VK_BLEND_OP_ADD;
switch( stateBits & GLS_BLENDOP_BITS )
{
case GLS_BLENDOP_MIN:
blendOp = VK_BLEND_OP_MIN;
break;
case GLS_BLENDOP_MAX:
blendOp = VK_BLEND_OP_MAX;
break;
case GLS_BLENDOP_ADD:
blendOp = VK_BLEND_OP_ADD;
break;
case GLS_BLENDOP_SUB:
blendOp = VK_BLEND_OP_SUBTRACT;
break;
}
attachmentState.blendEnable = ( srcFactor != VK_BLEND_FACTOR_ONE || dstFactor != VK_BLEND_FACTOR_ZERO );
attachmentState.colorBlendOp = blendOp;
attachmentState.srcColorBlendFactor = srcFactor;
attachmentState.dstColorBlendFactor = dstFactor;
attachmentState.alphaBlendOp = blendOp;
attachmentState.srcAlphaBlendFactor = srcFactor;
attachmentState.dstAlphaBlendFactor = dstFactor;
// Color Mask
attachmentState.colorWriteMask = 0;
attachmentState.colorWriteMask |= ( stateBits & GLS_REDMASK ) ? 0 : VK_COLOR_COMPONENT_R_BIT;
attachmentState.colorWriteMask |= ( stateBits & GLS_GREENMASK ) ? 0 : VK_COLOR_COMPONENT_G_BIT;
attachmentState.colorWriteMask |= ( stateBits & GLS_BLUEMASK ) ? 0 : VK_COLOR_COMPONENT_B_BIT;
attachmentState.colorWriteMask |= ( stateBits & GLS_ALPHAMASK ) ? 0 : VK_COLOR_COMPONENT_A_BIT;
}
// Color Blend
VkPipelineColorBlendStateCreateInfo colorBlendState = {};
colorBlendState.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
colorBlendState.attachmentCount = 1;
colorBlendState.pAttachments = &attachmentState;
// Depth / Stencil
VkPipelineDepthStencilStateCreateInfo depthStencilState = {};
{
VkCompareOp depthCompareOp = VK_COMPARE_OP_ALWAYS;
switch( stateBits & GLS_DEPTHFUNC_BITS )
{
case GLS_DEPTHFUNC_EQUAL:
depthCompareOp = VK_COMPARE_OP_EQUAL;
break;
case GLS_DEPTHFUNC_ALWAYS:
depthCompareOp = VK_COMPARE_OP_ALWAYS;
break;
case GLS_DEPTHFUNC_LESS:
depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
break;
case GLS_DEPTHFUNC_GREATER:
depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL;
break;
}
VkCompareOp stencilCompareOp = VK_COMPARE_OP_ALWAYS;
switch( stateBits & GLS_STENCIL_FUNC_BITS )
{
case GLS_STENCIL_FUNC_NEVER:
stencilCompareOp = VK_COMPARE_OP_NEVER;
break;
case GLS_STENCIL_FUNC_LESS:
stencilCompareOp = VK_COMPARE_OP_LESS;
break;
case GLS_STENCIL_FUNC_EQUAL:
stencilCompareOp = VK_COMPARE_OP_EQUAL;
break;
case GLS_STENCIL_FUNC_LEQUAL:
stencilCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
break;
case GLS_STENCIL_FUNC_GREATER:
stencilCompareOp = VK_COMPARE_OP_GREATER;
break;
case GLS_STENCIL_FUNC_NOTEQUAL:
stencilCompareOp = VK_COMPARE_OP_NOT_EQUAL;
break;
case GLS_STENCIL_FUNC_GEQUAL:
stencilCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL;
break;
case GLS_STENCIL_FUNC_ALWAYS:
stencilCompareOp = VK_COMPARE_OP_ALWAYS;
break;
}
depthStencilState.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthStencilState.depthTestEnable = VK_TRUE;
depthStencilState.depthWriteEnable = ( stateBits & GLS_DEPTHMASK ) == 0;
depthStencilState.depthCompareOp = depthCompareOp;
depthStencilState.depthBoundsTestEnable = vkcontext.physicalDeviceFeatures.depthBounds && ( stateBits & GLS_DEPTH_TEST_MASK ) != 0;
depthStencilState.minDepthBounds = 0.0f;
depthStencilState.maxDepthBounds = 1.0f;
depthStencilState.stencilTestEnable = ( stateBits & ( GLS_STENCIL_FUNC_BITS | GLS_STENCIL_OP_BITS | GLS_SEPARATE_STENCIL ) ) != 0;
uint32 ref = uint32( ( stateBits & GLS_STENCIL_FUNC_REF_BITS ) >> GLS_STENCIL_FUNC_REF_SHIFT );
uint32 mask = uint32( ( stateBits & GLS_STENCIL_FUNC_MASK_BITS ) >> GLS_STENCIL_FUNC_MASK_SHIFT );
if( stateBits & GLS_SEPARATE_STENCIL )
{
depthStencilState.front = GetStencilOpState( stateBits & GLS_STENCIL_FRONT_OPS );
depthStencilState.front.writeMask = 0xFFFFFFFF;
depthStencilState.front.compareOp = stencilCompareOp;
depthStencilState.front.compareMask = mask;
depthStencilState.front.reference = ref;
depthStencilState.back = GetStencilOpState( ( stateBits & GLS_STENCIL_BACK_OPS ) >> 12 );
depthStencilState.back.writeMask = 0xFFFFFFFF;
depthStencilState.back.compareOp = stencilCompareOp;
depthStencilState.back.compareMask = mask;
depthStencilState.back.reference = ref;
}
else
{
depthStencilState.front = GetStencilOpState( stateBits );
depthStencilState.front.writeMask = 0xFFFFFFFF;
depthStencilState.front.compareOp = stencilCompareOp;
depthStencilState.front.compareMask = mask;
depthStencilState.front.reference = ref;
depthStencilState.back = depthStencilState.front;
}
}
// Multisample
VkPipelineMultisampleStateCreateInfo multisampleState = {};
multisampleState.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisampleState.rasterizationSamples = vkcontext.sampleCount;
if( vkcontext.supersampling )
{
multisampleState.sampleShadingEnable = VK_TRUE;
multisampleState.minSampleShading = 1.0f;
}
// Shader Stages
idList< VkPipelineShaderStageCreateInfo > stages;
VkPipelineShaderStageCreateInfo stage = {};
stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stage.pName = "main";
{
stage.module = vertexShader;
stage.stage = VK_SHADER_STAGE_VERTEX_BIT;
stages.Append( stage );
}
if( fragmentShader != VK_NULL_HANDLE )
{
stage.module = fragmentShader;
stage.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
stages.Append( stage );
}
// Dynamic
idList< VkDynamicState > dynamic;
dynamic.Append( VK_DYNAMIC_STATE_SCISSOR );
dynamic.Append( VK_DYNAMIC_STATE_VIEWPORT );
//if( stateBits & GLS_POLYGON_OFFSET )
{
dynamic.Append( VK_DYNAMIC_STATE_DEPTH_BIAS );
}
//if( stateBits & GLS_DEPTH_TEST_MASK )
{
dynamic.Append( VK_DYNAMIC_STATE_DEPTH_BOUNDS );
}
VkPipelineDynamicStateCreateInfo dynamicState = {};
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.dynamicStateCount = dynamic.Num();
dynamicState.pDynamicStates = dynamic.Ptr();
// Viewport / Scissor
VkPipelineViewportStateCreateInfo viewportState = {};
viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewportState.viewportCount = 1;
viewportState.scissorCount = 1;
// Pipeline Create
VkGraphicsPipelineCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
createInfo.layout = pipelineLayout;
createInfo.renderPass = vkcontext.renderPass;
createInfo.pVertexInputState = &vertexInputState;
createInfo.pInputAssemblyState = &assemblyInputState;
createInfo.pRasterizationState = &rasterizationState;
createInfo.pColorBlendState = &colorBlendState;
createInfo.pDepthStencilState = &depthStencilState;
createInfo.pMultisampleState = &multisampleState;
createInfo.pDynamicState = &dynamicState;
createInfo.pViewportState = &viewportState;
createInfo.stageCount = stages.Num();
createInfo.pStages = stages.Ptr();
VkPipeline pipeline = VK_NULL_HANDLE;
ID_VK_CHECK( vkCreateGraphicsPipelines( vkcontext.device, vkcontext.pipelineCache, 1, &createInfo, NULL, &pipeline ) );
return pipeline;
}
/*
========================
renderProg_t::GetPipeline
========================
*/
VkPipeline idRenderProgManager::renderProg_t::GetPipeline( uint64 stateBits, VkShaderModule vertexShader, VkShaderModule fragmentShader )
{
for( int i = 0; i < pipelines.Num(); ++i )
{
if( stateBits == pipelines[ i ].stateBits )
{
return pipelines[ i ].pipeline;
}
}
VkPipeline pipeline = CreateGraphicsPipeline( vertexLayout, vertexShader, fragmentShader, pipelineLayout, stateBits );
pipelineState_t pipelineState;
pipelineState.pipeline = pipeline;
pipelineState.stateBits = stateBits;
pipelines.Append( pipelineState );
return pipeline;
}
/*
================================================================================================
idRenderProgManager::CommitUnforms
================================================================================================
*/
void idRenderProgManager::CommitUniforms( uint64 stateBits )
{
#if 0
const int progID = current;
const renderProg_t& prog = renderProgs[progID];
//GL_CheckErrors();
ALIGNTYPE16 idVec4 localVectors[RENDERPARM_TOTAL];
auto commitarray = [&]( idVec4( &vectors )[ RENDERPARM_TOTAL ] , shader_t& shader )
{
const int numUniforms = shader.uniforms.Num();
if( shader.uniformArray != -1 && numUniforms > 0 )
{
int totalUniforms = 0;
for( int i = 0; i < numUniforms; ++i )
{
// RB: HACK rpShadowMatrices[6 * 4]
if( shader.uniforms[i] == RENDERPARM_SHADOW_MATRIX_0_X )
{
for( int j = 0; j < ( 6 * 4 ); j++ )
{
vectors[i + j] = uniforms[ shader.uniforms[i] + j];
totalUniforms++;
}
}
else
{
vectors[i] = uniforms[ shader.uniforms[i] ];
totalUniforms++;
}
}
glUniform4fv( shader.uniformArray, totalUniforms, localVectors->ToFloatPtr() );
}
};
if( prog.vertexShaderIndex >= 0 )
{
commitarray( localVectors, shaders[ prog.vertexShaderIndex ] );
}
if( prog.fragmentShaderIndex >= 0 )
{
commitarray( localVectors, shaders[ prog.fragmentShaderIndex ] );
}
#endif
renderProg_t& prog = renderProgs[ current ];
VkPipeline pipeline = prog.GetPipeline( stateBits, shaders[ prog.vertexShaderIndex ].module, shaders[ prog.fragmentShaderIndex ].module );
VkDescriptorSetAllocateInfo setAllocInfo = {};
setAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
setAllocInfo.pNext = NULL;
setAllocInfo.descriptorPool = descriptorPools[ currentData ];
setAllocInfo.descriptorSetCount = 1;
setAllocInfo.pSetLayouts = &prog.descriptorSetLayout;
ID_VK_CHECK( vkAllocateDescriptorSets( vkcontext.device, &setAllocInfo, &descriptorSets[ currentData ][ currentDescSet ] ) );
VkDescriptorSet descSet = descriptorSets[ currentData ][ currentDescSet ];
currentDescSet++;
int writeIndex = 0;
int bufferIndex = 0;
int imageIndex = 0;
int bindingIndex = 0;
VkWriteDescriptorSet writes[ MAX_DESC_SET_WRITES ];
VkDescriptorBufferInfo bufferInfos[ MAX_DESC_SET_WRITES ];
VkDescriptorImageInfo imageInfos[ MAX_DESC_SET_WRITES ];
int uboIndex = 0;
idUniformBuffer* ubos[ 3 ] = { NULL, NULL, NULL };
idUniformBuffer vertParms;
if( prog.vertexShaderIndex > -1 && shaders[ prog.vertexShaderIndex ].parmIndices.Num() > 0 )
{
AllocParmBlockBuffer( shaders[ prog.vertexShaderIndex ].parmIndices, vertParms );
ubos[ uboIndex++ ] = &vertParms;
}
idUniformBuffer jointBuffer;
if( prog.usesJoints && vkcontext.jointCacheHandle > 0 )
{
if( !vertexCache.GetJointBuffer( vkcontext.jointCacheHandle, &jointBuffer ) )
{
idLib::Error( "idRenderProgManager::CommitCurrent: jointBuffer == NULL" );
return;
}
assert( ( jointBuffer.GetOffset() & ( vkcontext.gpu->props.limits.minUniformBufferOffsetAlignment - 1 ) ) == 0 );
ubos[ uboIndex++ ] = &jointBuffer;
}
else if( prog.optionalSkinning )
{
ubos[ uboIndex++ ] = &emptyUBO;
}
idUniformBuffer fragParms;
if( prog.fragmentShaderIndex > -1 && shaders[ prog.fragmentShaderIndex ].parmIndices.Num() > 0 )
{
AllocParmBlockBuffer( shaders[ prog.fragmentShaderIndex ].parmIndices, fragParms );
ubos[ uboIndex++ ] = &fragParms;
}
for( int i = 0; i < prog.bindings.Num(); ++i )
{
rpBinding_t binding = prog.bindings[ i ];
switch( binding )
{
case BINDING_TYPE_UNIFORM_BUFFER:
{
idUniformBuffer* ubo = ubos[ bufferIndex ];
VkDescriptorBufferInfo& bufferInfo = bufferInfos[ bufferIndex++ ];
memset( &bufferInfo, 0, sizeof( VkDescriptorBufferInfo ) );
bufferInfo.buffer = ubo->GetAPIObject();
bufferInfo.offset = ubo->GetOffset();
bufferInfo.range = ubo->GetSize();
VkWriteDescriptorSet& write = writes[ writeIndex++ ];
memset( &write, 0, sizeof( VkWriteDescriptorSet ) );
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = descSet;
write.dstBinding = bindingIndex++;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
write.pBufferInfo = &bufferInfo;
break;
}
case BINDING_TYPE_SAMPLER:
{
idImage* image = vkcontext.imageParms[ imageIndex ];
VkDescriptorImageInfo& imageInfo = imageInfos[ imageIndex++ ];
memset( &imageInfo, 0, sizeof( VkDescriptorImageInfo ) );
imageInfo.imageLayout = image->GetLayout();
imageInfo.imageView = image->GetView();
imageInfo.sampler = image->GetSampler();
assert( image->GetView() != VK_NULL_HANDLE );
VkWriteDescriptorSet& write = writes[ writeIndex++ ];
memset( &write, 0, sizeof( VkWriteDescriptorSet ) );
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write.dstSet = descSet;
write.dstBinding = bindingIndex++;
write.descriptorCount = 1;
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
write.pImageInfo = &imageInfo;
//imageIndex++;
break;
}
}
}
vkUpdateDescriptorSets( vkcontext.device, writeIndex, writes, 0, NULL );
vkCmdBindDescriptorSets(
vkcontext.commandBuffer[ vkcontext.frameParity ],
VK_PIPELINE_BIND_POINT_GRAPHICS,
prog.pipelineLayout, 0, 1, &descSet,
0, NULL );
vkCmdBindPipeline(
vkcontext.commandBuffer[ vkcontext.frameParity ],
VK_PIPELINE_BIND_POINT_GRAPHICS,
pipeline );
}
void idRenderProgManager::CachePipeline( uint64 stateBits )
{
renderProg_t& prog = renderProgs[ current ];
VkPipeline pipeline = prog.GetPipeline( stateBits, shaders[ prog.vertexShaderIndex ].module, shaders[ prog.fragmentShaderIndex ].module );
}
void idRenderProgManager::PrintPipelines()
{
for( int i = 0; i < renderProgManager.renderProgs.Num(); ++i )
{
renderProg_t& prog = renderProgManager.renderProgs[ i ];
for( int j = 0; j < prog.pipelines.Num(); ++j )
{
idLib::Printf( "%s: %llu\n", prog.name.c_str(), prog.pipelines[ j ].stateBits );
idLib::Printf( "------------------------------------------\n" );
RpPrintState( prog.pipelines[ j ].stateBits );
idLib::Printf( "\n" );
}
}
}
void idRenderProgManager::ClearPipelines()
{
for( int i = 0; i < renderProgManager.renderProgs.Num(); ++i )
{
renderProg_t& prog = renderProgManager.renderProgs[ i ];
for( int j = 0; j < prog.pipelines.Num(); ++j )
{
vkDestroyPipeline( vkcontext.device, prog.pipelines[ j ].pipeline, NULL );
}
prog.pipelines.Clear();
}
}
CONSOLE_COMMAND( Vulkan_PrintPipelineStates, "Print the GLState bits associated with each pipeline.", 0 )
{
renderProgManager.PrintPipelines();
}
CONSOLE_COMMAND( Vulkan_ClearPipelines, "Clear all existing pipelines, forcing them to be recreated.", 0 )
{
renderProgManager.ClearPipelines();
}