mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-08-10 08:41:04 +00:00
1392 lines
50 KiB
C++
1392 lines
50 KiB
C++
/******************************************************************************
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2018-2026 Baldur Karlsson
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
* THE SOFTWARE.
|
|
******************************************************************************/
|
|
|
|
#include "vk_shader_cache.h"
|
|
#include "common/shader_cache.h"
|
|
#include "data/glsl_shaders.h"
|
|
#include "strings/string_utils.h"
|
|
|
|
enum class FeatureCheck
|
|
{
|
|
NoCheck = 0x0,
|
|
ShaderMSAAStorage = 0x1,
|
|
FragmentStores = 0x2,
|
|
NonMetalBackend = 0x4,
|
|
FormatlessWrite = 0x8,
|
|
SampleShading = 0x10,
|
|
Geometry = 0x20,
|
|
MultiView = 0x40,
|
|
};
|
|
|
|
BITMASK_OPERATORS(FeatureCheck);
|
|
|
|
enum class BuiltinShaderFlags
|
|
{
|
|
None = 0x0,
|
|
BaseTypeParameterised = 0x1,
|
|
TextureTypeParameterised = 0x2,
|
|
Multiview = 0x4,
|
|
};
|
|
|
|
BITMASK_OPERATORS(BuiltinShaderFlags);
|
|
|
|
struct BuiltinShaderConfig
|
|
{
|
|
BuiltinShaderConfig(BuiltinShader builtin, EmbeddedResourceType resource,
|
|
rdcspv::ShaderStage stage, FeatureCheck checks = FeatureCheck::NoCheck,
|
|
BuiltinShaderFlags flags = BuiltinShaderFlags::None)
|
|
: builtin(builtin), resource(resource), stage(stage), checks(checks), flags(flags)
|
|
{
|
|
}
|
|
BuiltinShader builtin;
|
|
EmbeddedResourceType resource;
|
|
rdcspv::ShaderStage stage;
|
|
FeatureCheck checks;
|
|
BuiltinShaderFlags flags;
|
|
};
|
|
|
|
static const BuiltinShaderConfig builtinShaders[] = {
|
|
BuiltinShaderConfig(BuiltinShader::BlitVS, EmbeddedResource(glsl_blit_vert),
|
|
rdcspv::ShaderStage::Vertex),
|
|
BuiltinShaderConfig(BuiltinShader::CheckerboardFS, EmbeddedResource(glsl_checkerboard_frag),
|
|
rdcspv::ShaderStage::Fragment),
|
|
BuiltinShaderConfig(BuiltinShader::CheckerboardMultiviewFS,
|
|
EmbeddedResource(glsl_checkerboard_frag), rdcspv::ShaderStage::Fragment,
|
|
FeatureCheck::MultiView, BuiltinShaderFlags::Multiview),
|
|
BuiltinShaderConfig(BuiltinShader::TexDisplayFS, EmbeddedResource(glsl_texdisplay_frag),
|
|
rdcspv::ShaderStage::Fragment),
|
|
BuiltinShaderConfig(BuiltinShader::FixedColFS, EmbeddedResource(glsl_fixedcol_frag),
|
|
rdcspv::ShaderStage::Fragment),
|
|
BuiltinShaderConfig(BuiltinShader::TextVS, EmbeddedResource(glsl_vktext_vert),
|
|
rdcspv::ShaderStage::Vertex),
|
|
BuiltinShaderConfig(BuiltinShader::TextFS, EmbeddedResource(glsl_vktext_frag),
|
|
rdcspv::ShaderStage::Fragment),
|
|
BuiltinShaderConfig(BuiltinShader::MeshVS, EmbeddedResource(glsl_mesh_vert),
|
|
rdcspv::ShaderStage::Vertex),
|
|
BuiltinShaderConfig(BuiltinShader::MeshGS, EmbeddedResource(glsl_mesh_geom),
|
|
rdcspv::ShaderStage::Geometry),
|
|
BuiltinShaderConfig(BuiltinShader::MeshFS, EmbeddedResource(glsl_mesh_frag),
|
|
rdcspv::ShaderStage::Fragment),
|
|
BuiltinShaderConfig(BuiltinShader::MeshCS, EmbeddedResource(glsl_mesh_comp),
|
|
rdcspv::ShaderStage::Compute),
|
|
BuiltinShaderConfig(BuiltinShader::QuadResolveFS, EmbeddedResource(glsl_quadresolve_frag),
|
|
rdcspv::ShaderStage::Fragment, FeatureCheck::FragmentStores),
|
|
BuiltinShaderConfig(BuiltinShader::QuadResolveMultiviewFS,
|
|
EmbeddedResource(glsl_quadresolve_frag), rdcspv::ShaderStage::Fragment,
|
|
FeatureCheck::FragmentStores | FeatureCheck::MultiView,
|
|
BuiltinShaderFlags::Multiview),
|
|
BuiltinShaderConfig(BuiltinShader::QuadWriteFS, EmbeddedResource(glsl_quadwrite_frag),
|
|
rdcspv::ShaderStage::Fragment),
|
|
BuiltinShaderConfig(
|
|
BuiltinShader::QuadWriteMultiviewFS, EmbeddedResource(glsl_quadwrite_frag),
|
|
rdcspv::ShaderStage::Fragment,
|
|
FeatureCheck::FragmentStores | FeatureCheck::NonMetalBackend | FeatureCheck::MultiView,
|
|
BuiltinShaderFlags::Multiview),
|
|
BuiltinShaderConfig(BuiltinShader::TrisizeGS, EmbeddedResource(glsl_trisize_geom),
|
|
rdcspv::ShaderStage::Geometry),
|
|
BuiltinShaderConfig(BuiltinShader::TrisizeFS, EmbeddedResource(glsl_trisize_frag),
|
|
rdcspv::ShaderStage::Fragment),
|
|
BuiltinShaderConfig(BuiltinShader::TrisizeMultiviewFS, EmbeddedResource(glsl_trisize_frag),
|
|
rdcspv::ShaderStage::Fragment, FeatureCheck::MultiView,
|
|
BuiltinShaderFlags::Multiview),
|
|
BuiltinShaderConfig(BuiltinShader::TexRemap, EmbeddedResource(glsl_texremap_frag),
|
|
rdcspv::ShaderStage::Fragment, FeatureCheck::NoCheck,
|
|
BuiltinShaderFlags::BaseTypeParameterised),
|
|
BuiltinShaderConfig(BuiltinShader::PixelHistoryMSCopyCS,
|
|
EmbeddedResource(glsl_pixelhistory_mscopy_comp), rdcspv::ShaderStage::Compute),
|
|
BuiltinShaderConfig(BuiltinShader::PixelHistoryMSCopyDepthCS,
|
|
EmbeddedResource(glsl_pixelhistory_mscopy_depth_comp),
|
|
rdcspv::ShaderStage::Compute),
|
|
BuiltinShaderConfig(BuiltinShader::PixelHistoryPrimIDFS,
|
|
EmbeddedResource(glsl_pixelhistory_primid_frag),
|
|
rdcspv::ShaderStage::Fragment, FeatureCheck::Geometry),
|
|
BuiltinShaderConfig(BuiltinShader::ShaderDebugSampleVS,
|
|
EmbeddedResource(glsl_shaderdebug_sample_vert), rdcspv::ShaderStage::Vertex),
|
|
BuiltinShaderConfig(BuiltinShader::DiscardFS, EmbeddedResource(glsl_discard_frag),
|
|
rdcspv::ShaderStage::Fragment, FeatureCheck::NoCheck,
|
|
BuiltinShaderFlags::BaseTypeParameterised),
|
|
BuiltinShaderConfig(
|
|
BuiltinShader::HistogramCS, EmbeddedResource(glsl_histogram_comp),
|
|
rdcspv::ShaderStage::Compute, FeatureCheck::NoCheck,
|
|
BuiltinShaderFlags::BaseTypeParameterised | BuiltinShaderFlags::TextureTypeParameterised),
|
|
BuiltinShaderConfig(
|
|
BuiltinShader::MinMaxTileCS, EmbeddedResource(glsl_minmaxtile_comp),
|
|
rdcspv::ShaderStage::Compute, FeatureCheck::NoCheck,
|
|
BuiltinShaderFlags::BaseTypeParameterised | BuiltinShaderFlags::TextureTypeParameterised),
|
|
BuiltinShaderConfig(BuiltinShader::MinMaxResultCS, EmbeddedResource(glsl_minmaxresult_comp),
|
|
rdcspv::ShaderStage::Compute, FeatureCheck::NoCheck,
|
|
BuiltinShaderFlags::BaseTypeParameterised),
|
|
BuiltinShaderConfig(BuiltinShader::MS2BufferCS, EmbeddedResource(glsl_vk_ms2buffer_comp),
|
|
rdcspv::ShaderStage::Compute, FeatureCheck::NonMetalBackend),
|
|
BuiltinShaderConfig(BuiltinShader::DepthMS2BufferCS, EmbeddedResource(glsl_vk_depthms2buffer_comp),
|
|
rdcspv::ShaderStage::Compute, FeatureCheck::NonMetalBackend),
|
|
BuiltinShaderConfig(BuiltinShader::Buffer2MSCS, EmbeddedResource(glsl_vk_buffer2ms_comp),
|
|
rdcspv::ShaderStage::Compute,
|
|
FeatureCheck::ShaderMSAAStorage | FeatureCheck::FormatlessWrite |
|
|
FeatureCheck::NonMetalBackend),
|
|
BuiltinShaderConfig(BuiltinShader::DepthBuf2MSFS, EmbeddedResource(glsl_vk_depthbuf2ms_frag),
|
|
rdcspv::ShaderStage::Fragment,
|
|
FeatureCheck::SampleShading | FeatureCheck::NonMetalBackend),
|
|
BuiltinShaderConfig(BuiltinShader::DepthCopyFS, EmbeddedResource(glsl_depth_copy_frag),
|
|
rdcspv::ShaderStage::Fragment, FeatureCheck::FragmentStores),
|
|
BuiltinShaderConfig(BuiltinShader::DepthCopyMSFS, EmbeddedResource(glsl_depth_copyms_frag),
|
|
rdcspv::ShaderStage::Fragment,
|
|
FeatureCheck::SampleShading | FeatureCheck::FragmentStores),
|
|
};
|
|
|
|
RDCCOMPILE_ASSERT(ARRAY_COUNT(builtinShaders) == arraydim<BuiltinShader>(),
|
|
"Missing built-in shader config");
|
|
|
|
static bool PassesChecks(const BuiltinShaderConfig &config, const WrappedVulkan *driver,
|
|
const VkDriverInfo &driverVersion, const VkPhysicalDeviceFeatures &features)
|
|
{
|
|
if(config.checks & FeatureCheck::ShaderMSAAStorage)
|
|
{
|
|
if(driverVersion.TexelFetchBrokenDriver() || driverVersion.AMDStorageMSAABrokenDriver() ||
|
|
!features.shaderStorageImageMultisample)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(config.checks & FeatureCheck::FormatlessWrite)
|
|
{
|
|
if(!features.shaderStorageImageWriteWithoutFormat)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(config.checks & FeatureCheck::SampleShading)
|
|
{
|
|
if(!features.sampleRateShading)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(config.checks & FeatureCheck::FragmentStores)
|
|
{
|
|
if(!features.fragmentStoresAndAtomics)
|
|
return false;
|
|
}
|
|
|
|
if(config.checks & FeatureCheck::NonMetalBackend)
|
|
{
|
|
// for now we don't allow it at all - in future we could check on whether it's been enabled
|
|
// via a more advanced query
|
|
if(driverVersion.RunningOnMetal())
|
|
return false;
|
|
}
|
|
|
|
if(config.checks & FeatureCheck::Geometry)
|
|
{
|
|
if(!features.geometryShader)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(config.checks & FeatureCheck::MultiView)
|
|
{
|
|
if(!driver->MultiView())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if(config.stage == rdcspv::ShaderStage::Geometry && !features.geometryShader)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
struct VulkanBlobShaderCallbacks
|
|
{
|
|
bool Create(uint32_t size, byte *data, SPIRVBlob *ret) const
|
|
{
|
|
RDCASSERT(ret);
|
|
|
|
SPIRVBlob blob = new rdcarray<uint32_t>();
|
|
|
|
blob->resize(size / sizeof(uint32_t));
|
|
|
|
memcpy(&(*blob)[0], data, size);
|
|
|
|
*ret = blob;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Destroy(SPIRVBlob blob) const { delete blob; }
|
|
uint32_t GetSize(SPIRVBlob blob) const { return (uint32_t)(blob->size() * sizeof(uint32_t)); }
|
|
const byte *GetData(SPIRVBlob blob) const { return (const byte *)blob->data(); }
|
|
} VulkanShaderCacheCallbacks;
|
|
|
|
struct VkPipeCacheHeader
|
|
{
|
|
uint32_t length;
|
|
VkPipelineCacheHeaderVersion version;
|
|
uint32_t vendorID;
|
|
uint32_t deviceID;
|
|
byte uuid[VK_UUID_SIZE];
|
|
};
|
|
|
|
VulkanShaderCache::VulkanShaderCache(WrappedVulkan *driver)
|
|
{
|
|
// Load shader cache, if present
|
|
bool success = LoadShaderCache("vkshaders.cache", m_ShaderCacheMagic, m_ShaderCacheVersion,
|
|
m_ShaderCache, VulkanShaderCacheCallbacks);
|
|
|
|
// if we failed to load from the cache
|
|
m_ShaderCacheDirty = !success;
|
|
|
|
m_pDriver = driver;
|
|
m_Device = driver->GetDev();
|
|
|
|
SetCaching(true);
|
|
|
|
const VkDriverInfo &driverVersion = driver->GetDriverInfo();
|
|
const VkPhysicalDeviceFeatures &enabledFeatures = driver->GetDeviceEnabledFeatures();
|
|
const VkPhysicalDeviceFeatures &availFeatures = driver->GetDeviceAvailableFeatures();
|
|
|
|
rdcstr globalDefines = "#define HAS_BIT_CONVERSION 1\n";
|
|
if(driverVersion.TexelFetchBrokenDriver())
|
|
globalDefines += "#define NO_TEXEL_FETCH\n";
|
|
if(driverVersion.RunningOnMetal())
|
|
globalDefines += "#define METAL_BACKEND\n";
|
|
|
|
rdcspv::CompilationSettings compileSettings;
|
|
compileSettings.lang = rdcspv::InputLanguage::VulkanGLSL;
|
|
|
|
uint32_t toolCount = 0;
|
|
if(ObjDisp(driver->GetPhysDev())->GetPhysicalDeviceToolProperties != NULL)
|
|
{
|
|
ObjDisp(driver->GetPhysDev())
|
|
->GetPhysicalDeviceToolProperties(Unwrap(driver->GetPhysDev()), &toolCount, NULL);
|
|
rdcarray<VkPhysicalDeviceToolProperties> tools;
|
|
tools.resize(toolCount);
|
|
ObjDisp(driver->GetPhysDev())
|
|
->GetPhysicalDeviceToolProperties(Unwrap(driver->GetPhysDev()), &toolCount, tools.data());
|
|
|
|
for(const VkPhysicalDeviceToolProperties &t : tools)
|
|
{
|
|
if(rdcstr(t.name) == "RenderDoc")
|
|
{
|
|
RDCLOG("Enabling debug info for self-captured shaders");
|
|
compileSettings.debugInfo = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
m_Buffer2MSSupported = PassesChecks(builtinShaders[(size_t)BuiltinShader::Buffer2MSCS], driver,
|
|
driverVersion, availFeatures);
|
|
|
|
for(auto i : indices<BuiltinShader>())
|
|
{
|
|
const BuiltinShaderConfig &config = builtinShaders[i];
|
|
|
|
RDCASSERT(config.builtin == (BuiltinShader)i);
|
|
|
|
bool passesChecks = PassesChecks(config, driver, driverVersion, enabledFeatures);
|
|
|
|
if(!passesChecks)
|
|
continue;
|
|
|
|
size_t baseTypeCount = size_t(BuiltinShaderBaseType::First) + 1,
|
|
textureTypeCount = size_t(BuiltinShaderTextureType::First) + 1;
|
|
|
|
if(config.flags & BuiltinShaderFlags::BaseTypeParameterised)
|
|
baseTypeCount = (size_t)BuiltinShaderBaseType::Count;
|
|
if(config.flags & BuiltinShaderFlags::TextureTypeParameterised)
|
|
textureTypeCount = (size_t)BuiltinShaderTextureType::Count;
|
|
|
|
compileSettings.stage = config.stage;
|
|
|
|
// for shaders that aren't parameterised these loops will be a no-op that only iterates once,
|
|
// and fills in [First][First] entry.
|
|
for(size_t baseType = (size_t)BuiltinShaderBaseType::First; baseType < baseTypeCount; baseType++)
|
|
{
|
|
for(size_t textureType = (size_t)BuiltinShaderTextureType::First;
|
|
textureType < textureTypeCount; textureType++)
|
|
{
|
|
rdcstr defines = globalDefines;
|
|
|
|
defines += rdcstr("#define SHADER_RESTYPE ") + ToStr(textureType) + "\n";
|
|
defines += rdcstr("#define SHADER_BASETYPE ") + ToStr(baseType) + "\n";
|
|
|
|
if(config.flags & BuiltinShaderFlags::Multiview)
|
|
defines += rdcstr("#define USE_MULTIVIEW 1\n");
|
|
|
|
SPIRVBlob &blob = m_BuiltinShaderBlobs[i][baseType][textureType];
|
|
rdcstr source = GetDynamicEmbeddedResource(config.resource);
|
|
|
|
uint32_t inputHash = strhash(source.c_str());
|
|
inputHash = strhash(defines.c_str(), inputHash);
|
|
|
|
// bump this version if anything inside GenerateGLSLShader changes. This is used to
|
|
// determine if we can skip the call to GenerateGLSLShader (which calls out to glslang).
|
|
// Otherwise we'll use the cached SPIR-V generated by the previous call using the same
|
|
// source & defines.
|
|
inputHash = strhash("inputHashVersion2", inputHash);
|
|
|
|
if(compileSettings.debugInfo)
|
|
inputHash = strhash("debug", inputHash);
|
|
|
|
rdcstr err;
|
|
|
|
if(m_ShaderCache.find(inputHash) != m_ShaderCache.end())
|
|
blob = m_ShaderCache[inputHash];
|
|
|
|
if(blob == NULL)
|
|
{
|
|
err = GetSPIRVBlob(compileSettings,
|
|
GenerateGLSLShader(source, ShaderType::Vulkan, 430, defines), blob);
|
|
|
|
// if we missed the inputHash, make a copy there too.
|
|
if(m_CacheShaders && blob)
|
|
{
|
|
m_ShaderCache[inputHash] = new rdcarray<uint32_t>(*blob);
|
|
m_ShaderCacheDirty = true;
|
|
}
|
|
}
|
|
|
|
if(!err.empty() || blob == VK_NULL_HANDLE)
|
|
{
|
|
RDCERR("Error compiling builtin %u (baseType %zu textureType %zu): %s", (uint32_t)i,
|
|
baseType, textureType, err.c_str());
|
|
}
|
|
else
|
|
{
|
|
VkShaderModuleCreateInfo modinfo = {
|
|
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
|
NULL,
|
|
0,
|
|
blob->size() * sizeof(uint32_t),
|
|
blob->data(),
|
|
};
|
|
|
|
VkResult vkr = driver->vkCreateShaderModule(
|
|
m_Device, &modinfo, NULL, &m_BuiltinShaderModules[i][baseType][textureType]);
|
|
CHECK_VKR(driver, vkr);
|
|
|
|
driver->GetResourceManager()->SetInternalResource(
|
|
GetResID(m_BuiltinShaderModules[i][baseType][textureType]));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
VkPipelineCacheCreateInfo createInfo = {VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO};
|
|
|
|
GetPipeCacheBlob();
|
|
|
|
if(!m_PipeCacheBlob.empty())
|
|
{
|
|
if(m_PipeCacheBlob.size() < sizeof(VkPipeCacheHeader))
|
|
{
|
|
m_PipeCacheBlob.clear();
|
|
}
|
|
else
|
|
{
|
|
VkPipeCacheHeader *header = (VkPipeCacheHeader *)m_PipeCacheBlob.data();
|
|
|
|
// check explicitly for incompatibility
|
|
if(header->length != sizeof(VkPipeCacheHeader))
|
|
{
|
|
m_PipeCacheBlob.clear();
|
|
RDCLOG("Pipeline cache header length %u is unexpected, not using cache", header->length);
|
|
}
|
|
else if(header->version != VK_PIPELINE_CACHE_HEADER_VERSION_ONE)
|
|
{
|
|
m_PipeCacheBlob.clear();
|
|
RDCLOG("Pipeline cache header version %u is unexpected, not using cache", header->version);
|
|
}
|
|
else if(header->vendorID != m_pDriver->GetDeviceProps().vendorID)
|
|
{
|
|
m_PipeCacheBlob.clear();
|
|
RDCLOG("Pipeline cache header vendorID %u doesn't match %u", header->vendorID,
|
|
m_pDriver->GetDeviceProps().vendorID);
|
|
}
|
|
else if(header->deviceID != m_pDriver->GetDeviceProps().deviceID)
|
|
{
|
|
m_PipeCacheBlob.clear();
|
|
RDCLOG("Pipeline cache header deviceID %u doesn't match %u", header->deviceID,
|
|
m_pDriver->GetDeviceProps().deviceID);
|
|
}
|
|
else if(memcmp(header->uuid, m_pDriver->GetDeviceProps().pipelineCacheUUID, VK_UUID_SIZE) != 0)
|
|
{
|
|
m_PipeCacheBlob.clear();
|
|
RDCLOG("Pipeline cache UUID doesn't match");
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!m_PipeCacheBlob.empty())
|
|
{
|
|
createInfo.initialDataSize = m_PipeCacheBlob.size();
|
|
createInfo.pInitialData = m_PipeCacheBlob.data();
|
|
}
|
|
|
|
// manually wrap the cache so the data is uploaded
|
|
|
|
VkResult vkr = ObjDisp(m_Device)->CreatePipelineCache(Unwrap(m_Device), &createInfo, NULL,
|
|
&m_PipelineCache);
|
|
CHECK_VKR(driver, vkr);
|
|
|
|
if(vkr == VK_SUCCESS)
|
|
{
|
|
ResourceId id = m_pDriver->GetResourceManager()->WrapResource(ResourceId(), Unwrap(m_Device),
|
|
m_PipelineCache);
|
|
|
|
if(IsCaptureMode(m_pDriver->GetState()))
|
|
{
|
|
m_pDriver->GetResourceManager()->AddResourceRecord(m_PipelineCache);
|
|
}
|
|
}
|
|
}
|
|
|
|
SetCaching(false);
|
|
}
|
|
|
|
VulkanShaderCache::~VulkanShaderCache()
|
|
{
|
|
if(m_PipelineCache != VK_NULL_HANDLE)
|
|
{
|
|
bytebuf blob;
|
|
size_t size = 0;
|
|
ObjDisp(m_Device)->GetPipelineCacheData(Unwrap(m_Device), Unwrap(m_PipelineCache), &size, NULL);
|
|
blob.resize(size);
|
|
ObjDisp(m_Device)->GetPipelineCacheData(Unwrap(m_Device), Unwrap(m_PipelineCache), &size,
|
|
blob.data());
|
|
SetPipeCacheBlob(blob);
|
|
m_pDriver->vkDestroyPipelineCache(m_Device, m_PipelineCache, NULL);
|
|
}
|
|
|
|
if(m_ShaderCacheDirty)
|
|
{
|
|
SaveShaderCache("vkshaders.cache", m_ShaderCacheMagic, m_ShaderCacheVersion, m_ShaderCache,
|
|
VulkanShaderCacheCallbacks);
|
|
}
|
|
else
|
|
{
|
|
for(auto it = m_ShaderCache.begin(); it != m_ShaderCache.end(); ++it)
|
|
VulkanShaderCacheCallbacks.Destroy(it->second);
|
|
}
|
|
|
|
for(size_t i = 0; i < ARRAY_COUNT(m_BuiltinShaderModules); i++)
|
|
for(size_t b = 0; b < ARRAY_COUNT(m_BuiltinShaderModules[0]); b++)
|
|
for(size_t t = 0; t < ARRAY_COUNT(m_BuiltinShaderModules[0][0]); t++)
|
|
m_pDriver->vkDestroyShaderModule(m_Device, m_BuiltinShaderModules[i][b][t], NULL);
|
|
}
|
|
|
|
rdcstr VulkanShaderCache::GetSPIRVBlob(const rdcspv::CompilationSettings &settings,
|
|
const rdcstr &src, SPIRVBlob &outBlob)
|
|
{
|
|
RDCASSERT(!src.empty());
|
|
|
|
uint32_t hash = strhash(src.c_str());
|
|
|
|
char typestr[3] = {'a', 'a', 0};
|
|
typestr[0] += (char)settings.stage;
|
|
typestr[1] += (char)settings.lang;
|
|
hash = strhash(typestr, hash);
|
|
|
|
if(m_ShaderCache.find(hash) != m_ShaderCache.end())
|
|
{
|
|
outBlob = m_ShaderCache[hash];
|
|
return "";
|
|
}
|
|
|
|
SPIRVBlob spirv = new rdcarray<uint32_t>();
|
|
rdcstr errors = rdcspv::Compile(settings, {src}, *spirv);
|
|
|
|
if(!errors.empty())
|
|
{
|
|
rdcstr logerror = errors;
|
|
if(logerror.length() > 1024)
|
|
logerror = logerror.substr(0, 1024) + "...";
|
|
|
|
RDCWARN("Shader compile error:\n%s", logerror.c_str());
|
|
|
|
delete spirv;
|
|
outBlob = NULL;
|
|
return errors;
|
|
}
|
|
|
|
outBlob = spirv;
|
|
|
|
if(m_CacheShaders)
|
|
{
|
|
m_ShaderCache[hash] = spirv;
|
|
m_ShaderCacheDirty = true;
|
|
}
|
|
|
|
return errors;
|
|
}
|
|
|
|
void VulkanShaderCache::GetPipeCacheBlob()
|
|
{
|
|
m_PipeCacheBlob.clear();
|
|
|
|
uint32_t hash = strhash(StringFormat::Fmt("PipelineCache%x%x", m_pDriver->GetDeviceProps().vendorID,
|
|
m_pDriver->GetDeviceProps().deviceID)
|
|
.c_str());
|
|
|
|
auto it = m_ShaderCache.find(hash);
|
|
|
|
if(it != m_ShaderCache.end())
|
|
{
|
|
SPIRVBlob blob = it->second;
|
|
|
|
// first uint32_t is the real byte size, since we rounded up to the nearest uint32 to store in a
|
|
// SPIRVBlob
|
|
uint32_t size = blob->at(0);
|
|
if(size == rdcspv::MagicNumber)
|
|
{
|
|
RDCLOG("Hash collision - pipeline cache trampled");
|
|
return;
|
|
}
|
|
m_PipeCacheBlob.resize(size);
|
|
memcpy(m_PipeCacheBlob.data(), blob->data() + 1, m_PipeCacheBlob.size());
|
|
}
|
|
}
|
|
|
|
void VulkanShaderCache::SetPipeCacheBlob(bytebuf &blob)
|
|
{
|
|
if(m_PipeCacheBlob == blob)
|
|
return;
|
|
|
|
VkPipeCacheHeader *header = (VkPipeCacheHeader *)blob.data();
|
|
|
|
uint32_t hash =
|
|
strhash(StringFormat::Fmt("PipelineCache%x%x", header->vendorID, header->deviceID).c_str());
|
|
|
|
rdcarray<uint32_t> *spirvBlob = new rdcarray<uint32_t>();
|
|
|
|
// align the size up to the nearest 4, and add one extra for us to store the real byte size
|
|
spirvBlob->resize(AlignUp4(blob.size()) / 4 + 1);
|
|
|
|
// store the size, then the real data after that
|
|
(*spirvBlob)[0] = (uint32_t)blob.size();
|
|
memcpy(spirvBlob->data() + 1, blob.data(), blob.size());
|
|
|
|
m_ShaderCache[hash] = spirvBlob;
|
|
m_ShaderCacheDirty = true;
|
|
}
|
|
|
|
void VulkanShaderCache::MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &pipeCreateInfo,
|
|
ResourceId pipeline)
|
|
{
|
|
// skip if invalid pipeline
|
|
if(pipeline == ResourceId())
|
|
{
|
|
RDCEraseEl(pipeCreateInfo);
|
|
return;
|
|
}
|
|
|
|
const VulkanCreationInfo::Pipeline &pipeInfo = m_pDriver->m_CreationInfo.m_Pipeline[pipeline];
|
|
|
|
VulkanResourceManager *rm = m_pDriver->GetResourceManager();
|
|
|
|
static VkPipelineShaderStageCreateInfo stages[NumShaderStages];
|
|
static VkSpecializationInfo specInfo[NumShaderStages];
|
|
static rdcarray<VkSpecializationMapEntry> specMapEntries;
|
|
|
|
// the specialization constants can't use more than a uint64_t, so we just over-allocate
|
|
static rdcarray<uint64_t> specdata;
|
|
|
|
size_t specEntries = 0;
|
|
|
|
for(uint32_t i = 0; i < NumShaderStages; i++)
|
|
specEntries += pipeInfo.shaders[i].specialization.size();
|
|
|
|
specMapEntries.resize(specEntries);
|
|
specdata.resize(specEntries);
|
|
|
|
VkSpecializationMapEntry *entry = specMapEntries.data();
|
|
uint64_t *data = specdata.data();
|
|
|
|
uint32_t stageCount = 0;
|
|
uint32_t dataOffset = 0;
|
|
|
|
static VkPipelineShaderStageRequiredSubgroupSizeCreateInfo reqSubgroupSize[NumShaderStages] = {};
|
|
static VkPipelineRobustnessCreateInfo shaderRobustness[NumShaderStages] = {};
|
|
|
|
// reserve space for spec constants
|
|
for(uint32_t i = 0; i < NumShaderStages; i++)
|
|
{
|
|
if(pipeInfo.shaders[i].module != ResourceId())
|
|
{
|
|
stages[stageCount].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
stages[stageCount].stage = (VkShaderStageFlagBits)(1 << i);
|
|
stages[stageCount].module = rm->GetHandle<VkShaderModule>(pipeInfo.shaders[i].module);
|
|
stages[stageCount].pName = pipeInfo.shaders[i].entryPoint.c_str();
|
|
stages[stageCount].pNext = NULL;
|
|
stages[stageCount].pSpecializationInfo = NULL;
|
|
stages[stageCount].flags = pipeInfo.shaders[i].flags;
|
|
|
|
if(pipeInfo.shaders[i].requiredSubgroupSize != 0)
|
|
{
|
|
reqSubgroupSize[i].requiredSubgroupSize = pipeInfo.shaders[i].requiredSubgroupSize;
|
|
|
|
reqSubgroupSize[i].sType =
|
|
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO;
|
|
reqSubgroupSize[i].pNext = (void *)stages[stageCount].pNext;
|
|
stages[stageCount].pNext = &reqSubgroupSize[i];
|
|
}
|
|
|
|
if(pipeInfo.shaders[i].HasRobustness())
|
|
{
|
|
shaderRobustness[i].images = pipeInfo.shaders[i].imageRobustness;
|
|
shaderRobustness[i].uniformBuffers = pipeInfo.shaders[i].uniformBufferRobustness;
|
|
shaderRobustness[i].storageBuffers = pipeInfo.shaders[i].storageBufferRobustness;
|
|
|
|
shaderRobustness[i].sType = VK_STRUCTURE_TYPE_PIPELINE_ROBUSTNESS_CREATE_INFO;
|
|
shaderRobustness[i].pNext = stages[stageCount].pNext;
|
|
stages[stageCount].pNext = &shaderRobustness;
|
|
}
|
|
|
|
if(!pipeInfo.shaders[i].specialization.empty())
|
|
{
|
|
stages[stageCount].pSpecializationInfo = &specInfo[i];
|
|
specInfo[i].pMapEntries = entry;
|
|
specInfo[i].mapEntryCount = (uint32_t)pipeInfo.shaders[i].specialization.size();
|
|
|
|
for(size_t s = 0; s < pipeInfo.shaders[i].specialization.size(); s++)
|
|
{
|
|
entry[s].constantID = pipeInfo.shaders[i].specialization[s].specID;
|
|
entry[s].size = pipeInfo.shaders[i].specialization[s].dataSize;
|
|
entry[s].offset = dataOffset * sizeof(uint64_t);
|
|
|
|
data[dataOffset] = pipeInfo.shaders[i].specialization[s].value;
|
|
|
|
dataOffset++;
|
|
}
|
|
|
|
specInfo[i].dataSize = specdata.size() * sizeof(uint64_t);
|
|
specInfo[i].pData = specdata.data();
|
|
|
|
entry += specInfo[i].mapEntryCount;
|
|
}
|
|
|
|
stageCount++;
|
|
}
|
|
}
|
|
|
|
static VkPipelineVertexInputStateCreateInfo vi = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
|
};
|
|
|
|
vi.pNext = NULL;
|
|
|
|
static VkVertexInputAttributeDescription viattr[128] = {};
|
|
static VkVertexInputBindingDescription vibind[128] = {};
|
|
|
|
vi.pVertexAttributeDescriptions = viattr;
|
|
vi.pVertexBindingDescriptions = vibind;
|
|
|
|
vi.vertexAttributeDescriptionCount = (uint32_t)pipeInfo.vertexAttrs.size();
|
|
vi.vertexBindingDescriptionCount = (uint32_t)pipeInfo.vertexBindings.size();
|
|
|
|
for(uint32_t i = 0; i < vi.vertexAttributeDescriptionCount; i++)
|
|
{
|
|
viattr[i].binding = pipeInfo.vertexAttrs[i].binding;
|
|
viattr[i].offset = pipeInfo.vertexAttrs[i].byteoffset;
|
|
viattr[i].format = pipeInfo.vertexAttrs[i].format;
|
|
viattr[i].location = pipeInfo.vertexAttrs[i].location;
|
|
}
|
|
|
|
for(uint32_t i = 0; i < vi.vertexBindingDescriptionCount; i++)
|
|
{
|
|
vibind[i].binding = pipeInfo.vertexBindings[i].vbufferBinding;
|
|
vibind[i].stride = pipeInfo.vertexBindings[i].bytestride;
|
|
vibind[i].inputRate = pipeInfo.vertexBindings[i].perInstance ? VK_VERTEX_INPUT_RATE_INSTANCE
|
|
: VK_VERTEX_INPUT_RATE_VERTEX;
|
|
}
|
|
|
|
static VkPipelineVertexInputDivisorStateCreateInfo vertexDivisor = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO,
|
|
};
|
|
static VkVertexInputBindingDivisorDescription vibindDivisors[128] = {};
|
|
|
|
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_EXT_vertex_attribute_divisor ||
|
|
m_pDriver->GetExtensions(GetRecord(m_Device)).ext_KHR_vertex_attribute_divisor)
|
|
{
|
|
vertexDivisor.pVertexBindingDivisors = vibindDivisors;
|
|
vertexDivisor.vertexBindingDivisorCount = 0;
|
|
|
|
for(uint32_t i = 0; i < vi.vertexBindingDescriptionCount; i++)
|
|
{
|
|
if(pipeInfo.vertexBindings[i].perInstance)
|
|
{
|
|
uint32_t instIdx = vertexDivisor.vertexBindingDivisorCount++;
|
|
vibindDivisors[instIdx].binding = pipeInfo.vertexBindings[i].vbufferBinding;
|
|
vibindDivisors[instIdx].divisor = pipeInfo.vertexBindings[i].instanceDivisor;
|
|
}
|
|
}
|
|
|
|
if(vertexDivisor.vertexBindingDivisorCount > 0)
|
|
{
|
|
vertexDivisor.pNext = vi.pNext;
|
|
vi.pNext = &vertexDivisor;
|
|
}
|
|
}
|
|
|
|
RDCASSERT(ARRAY_COUNT(viattr) >= pipeInfo.vertexAttrs.size());
|
|
RDCASSERT(ARRAY_COUNT(vibind) >= pipeInfo.vertexBindings.size());
|
|
|
|
static VkPipelineInputAssemblyStateCreateInfo ia = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
|
|
|
|
ia.pNext = NULL;
|
|
|
|
ia.topology = pipeInfo.topology;
|
|
ia.primitiveRestartEnable = pipeInfo.primitiveRestartEnable;
|
|
|
|
static VkPipelineTessellationStateCreateInfo tess = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO};
|
|
|
|
tess.pNext = NULL;
|
|
|
|
tess.patchControlPoints = pipeInfo.patchControlPoints;
|
|
|
|
static VkPipelineTessellationDomainOriginStateCreateInfo tessDomain = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_DOMAIN_ORIGIN_STATE_CREATE_INFO,
|
|
};
|
|
|
|
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_KHR_maintenance2)
|
|
{
|
|
tessDomain.domainOrigin = pipeInfo.tessellationDomainOrigin;
|
|
|
|
tessDomain.pNext = tess.pNext;
|
|
tess.pNext = &tessDomain;
|
|
}
|
|
|
|
static VkPipelineViewportStateCreateInfo vp = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
|
|
static VkViewport views[32] = {};
|
|
static VkRect2D scissors[32] = {};
|
|
|
|
vp.pNext = NULL;
|
|
|
|
memcpy(views, &pipeInfo.viewports[0], pipeInfo.viewports.size() * sizeof(VkViewport));
|
|
|
|
vp.pViewports = &views[0];
|
|
vp.viewportCount = (uint32_t)pipeInfo.viewports.size();
|
|
|
|
memcpy(scissors, &pipeInfo.scissors[0], pipeInfo.scissors.size() * sizeof(VkRect2D));
|
|
|
|
vp.pScissors = &scissors[0];
|
|
vp.scissorCount = (uint32_t)pipeInfo.scissors.size();
|
|
|
|
RDCASSERT(ARRAY_COUNT(views) >= pipeInfo.viewports.size());
|
|
RDCASSERT(ARRAY_COUNT(scissors) >= pipeInfo.scissors.size());
|
|
|
|
static VkPipelineViewportDepthClipControlCreateInfoEXT depthClipControl = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_DEPTH_CLIP_CONTROL_CREATE_INFO_EXT,
|
|
};
|
|
|
|
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_EXT_depth_clip_control)
|
|
{
|
|
depthClipControl.negativeOneToOne = pipeInfo.negativeOneToOne;
|
|
|
|
depthClipControl.pNext = vp.pNext;
|
|
vp.pNext = &depthClipControl;
|
|
}
|
|
|
|
static VkPipelineRasterizationStateCreateInfo rs = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
|
};
|
|
|
|
rs.pNext = NULL;
|
|
|
|
rs.depthClampEnable = pipeInfo.depthClampEnable;
|
|
rs.rasterizerDiscardEnable = pipeInfo.rasterizerDiscardEnable;
|
|
rs.polygonMode = pipeInfo.polygonMode;
|
|
rs.cullMode = pipeInfo.cullMode;
|
|
rs.frontFace = pipeInfo.frontFace;
|
|
rs.depthBiasEnable = pipeInfo.depthBiasEnable;
|
|
rs.depthBiasConstantFactor = pipeInfo.depthBiasConstantFactor;
|
|
rs.depthBiasClamp = pipeInfo.depthBiasClamp;
|
|
rs.depthBiasSlopeFactor = pipeInfo.depthBiasSlopeFactor;
|
|
rs.lineWidth = pipeInfo.lineWidth;
|
|
|
|
static VkDepthBiasRepresentationInfoEXT depthBiasRepr = {
|
|
VK_STRUCTURE_TYPE_DEPTH_BIAS_REPRESENTATION_INFO_EXT,
|
|
};
|
|
|
|
if(m_pDriver->DepthBiasControl())
|
|
{
|
|
depthBiasRepr.depthBiasRepresentation = pipeInfo.depthBiasRepresentation;
|
|
depthBiasRepr.depthBiasExact = pipeInfo.depthBiasExact;
|
|
|
|
depthBiasRepr.pNext = rs.pNext;
|
|
rs.pNext = &depthBiasRepr;
|
|
}
|
|
|
|
static VkPipelineRasterizationConservativeStateCreateInfoEXT conservRast = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_CONSERVATIVE_STATE_CREATE_INFO_EXT,
|
|
};
|
|
|
|
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_EXT_conservative_rasterization)
|
|
{
|
|
conservRast.conservativeRasterizationMode = pipeInfo.conservativeRasterizationMode;
|
|
conservRast.extraPrimitiveOverestimationSize = pipeInfo.extraPrimitiveOverestimationSize;
|
|
|
|
conservRast.pNext = rs.pNext;
|
|
rs.pNext = &conservRast;
|
|
}
|
|
|
|
static VkPipelineRasterizationStateStreamCreateInfoEXT rastStream = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_STREAM_CREATE_INFO_EXT,
|
|
};
|
|
|
|
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_EXT_transform_feedback &&
|
|
pipeInfo.rasterizationStream != 0)
|
|
{
|
|
rastStream.rasterizationStream = pipeInfo.rasterizationStream;
|
|
|
|
rastStream.pNext = rs.pNext;
|
|
rs.pNext = &rastStream;
|
|
}
|
|
|
|
static VkPipelineRasterizationDepthClipStateCreateInfoEXT depthClipState = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_DEPTH_CLIP_STATE_CREATE_INFO_EXT,
|
|
};
|
|
|
|
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_EXT_depth_clip_enable)
|
|
{
|
|
depthClipState.depthClipEnable = pipeInfo.depthClipEnable;
|
|
|
|
depthClipState.pNext = rs.pNext;
|
|
rs.pNext = &depthClipState;
|
|
}
|
|
|
|
static VkPipelineRasterizationLineStateCreateInfo lineRasterState = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_LINE_STATE_CREATE_INFO,
|
|
};
|
|
|
|
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_EXT_line_rasterization ||
|
|
m_pDriver->GetExtensions(GetRecord(m_Device)).ext_KHR_line_rasterization)
|
|
{
|
|
lineRasterState.lineRasterizationMode = pipeInfo.lineRasterMode;
|
|
lineRasterState.stippledLineEnable = pipeInfo.stippleEnabled;
|
|
lineRasterState.lineStippleFactor = pipeInfo.stippleFactor;
|
|
lineRasterState.lineStipplePattern = pipeInfo.stipplePattern;
|
|
|
|
lineRasterState.pNext = rs.pNext;
|
|
rs.pNext = &lineRasterState;
|
|
}
|
|
|
|
static VkPipelineMultisampleStateCreateInfo msaa = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
|
|
};
|
|
|
|
msaa.pNext = NULL;
|
|
|
|
msaa.rasterizationSamples = pipeInfo.rasterizationSamples;
|
|
msaa.sampleShadingEnable = pipeInfo.sampleShadingEnable;
|
|
msaa.minSampleShading = pipeInfo.minSampleShading;
|
|
msaa.pSampleMask = &pipeInfo.sampleMask;
|
|
msaa.alphaToCoverageEnable = pipeInfo.alphaToCoverageEnable;
|
|
msaa.alphaToOneEnable = pipeInfo.alphaToOneEnable;
|
|
|
|
static VkPipelineSampleLocationsStateCreateInfoEXT sampleLoc = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_SAMPLE_LOCATIONS_STATE_CREATE_INFO_EXT,
|
|
};
|
|
|
|
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_EXT_sample_locations &&
|
|
pipeInfo.sampleLocations.enabled)
|
|
{
|
|
sampleLoc.sampleLocationsEnable = pipeInfo.sampleLocations.enabled;
|
|
sampleLoc.sampleLocationsInfo.sType = VK_STRUCTURE_TYPE_SAMPLE_LOCATIONS_INFO_EXT;
|
|
sampleLoc.sampleLocationsInfo.sampleLocationGridSize = pipeInfo.sampleLocations.gridSize;
|
|
sampleLoc.sampleLocationsInfo.sampleLocationsPerPixel = pipeInfo.rasterizationSamples;
|
|
sampleLoc.sampleLocationsInfo.sampleLocationsCount =
|
|
(uint32_t)pipeInfo.sampleLocations.locations.size();
|
|
sampleLoc.sampleLocationsInfo.pSampleLocations = pipeInfo.sampleLocations.locations.data();
|
|
|
|
sampleLoc.pNext = msaa.pNext;
|
|
msaa.pNext = &sampleLoc;
|
|
}
|
|
|
|
static VkPipelineDepthStencilStateCreateInfo ds = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO};
|
|
|
|
ds.pNext = NULL;
|
|
|
|
ds.depthTestEnable = pipeInfo.depthTestEnable;
|
|
ds.depthWriteEnable = pipeInfo.depthWriteEnable;
|
|
ds.depthCompareOp = pipeInfo.depthCompareOp;
|
|
ds.depthBoundsTestEnable = pipeInfo.depthBoundsEnable;
|
|
ds.stencilTestEnable = pipeInfo.stencilTestEnable;
|
|
ds.front = pipeInfo.front;
|
|
ds.back = pipeInfo.back;
|
|
ds.minDepthBounds = pipeInfo.minDepthBounds;
|
|
ds.maxDepthBounds = pipeInfo.maxDepthBounds;
|
|
|
|
static VkPipelineColorBlendStateCreateInfo cb = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
|
|
|
|
cb.pNext = NULL;
|
|
|
|
cb.logicOpEnable = pipeInfo.logicOpEnable;
|
|
cb.logicOp = pipeInfo.logicOp;
|
|
memcpy(cb.blendConstants, pipeInfo.blendConst, sizeof(cb.blendConstants));
|
|
|
|
static VkPipelineColorBlendAttachmentState atts[32] = {};
|
|
|
|
cb.attachmentCount = (uint32_t)pipeInfo.attachments.size();
|
|
cb.pAttachments = atts;
|
|
|
|
for(uint32_t i = 0; i < cb.attachmentCount; i++)
|
|
{
|
|
atts[i].blendEnable = pipeInfo.attachments[i].blendEnable;
|
|
atts[i].colorWriteMask = pipeInfo.attachments[i].channelWriteMask;
|
|
atts[i].alphaBlendOp = pipeInfo.attachments[i].alphaBlend.Operation;
|
|
atts[i].srcAlphaBlendFactor = pipeInfo.attachments[i].alphaBlend.Source;
|
|
atts[i].dstAlphaBlendFactor = pipeInfo.attachments[i].alphaBlend.Destination;
|
|
atts[i].colorBlendOp = pipeInfo.attachments[i].blend.Operation;
|
|
atts[i].srcColorBlendFactor = pipeInfo.attachments[i].blend.Source;
|
|
atts[i].dstColorBlendFactor = pipeInfo.attachments[i].blend.Destination;
|
|
}
|
|
|
|
RDCASSERT(ARRAY_COUNT(atts) >= pipeInfo.attachments.size());
|
|
|
|
static VkDynamicState dynSt[VkDynamicCount];
|
|
|
|
static VkPipelineDynamicStateCreateInfo dyn = {VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
|
|
|
|
dyn.pNext = NULL;
|
|
|
|
dyn.dynamicStateCount = 0;
|
|
dyn.pDynamicStates = dynSt;
|
|
|
|
for(uint32_t i = 0; i < VkDynamicCount; i++)
|
|
if(pipeInfo.dynamicStates[i])
|
|
dynSt[dyn.dynamicStateCount++] = ConvertDynamicState((VulkanDynamicStateIndex)i);
|
|
|
|
// since we don't have to worry about threading, we point everything at the above static structs
|
|
|
|
VkGraphicsPipelineCreateInfo ret = {
|
|
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
|
NULL,
|
|
0,
|
|
stageCount,
|
|
stages,
|
|
&vi,
|
|
&ia,
|
|
&tess,
|
|
&vp,
|
|
&rs,
|
|
&msaa,
|
|
&ds,
|
|
&cb,
|
|
&dyn,
|
|
VK_NULL_HANDLE,
|
|
rm->GetHandle<VkRenderPass>(pipeInfo.renderpass),
|
|
pipeInfo.subpass,
|
|
VK_NULL_HANDLE, // base pipeline handle
|
|
0, // base pipeline index
|
|
};
|
|
|
|
// if the layouts are the same object (non-library case) we can just use it directly
|
|
if(pipeInfo.vertLayout == pipeInfo.fragLayout)
|
|
{
|
|
ret.layout = rm->GetHandle<VkPipelineLayout>(pipeInfo.vertLayout);
|
|
}
|
|
else
|
|
{
|
|
ret.layout = m_CombinedPipeLayouts[pipeline];
|
|
if(ret.layout == VK_NULL_HANDLE)
|
|
{
|
|
rdcarray<VkDescriptorSetLayout> descSetLayouts;
|
|
|
|
for(ResourceId setLayout : pipeInfo.descSetLayouts)
|
|
descSetLayouts.push_back(rm->GetHandle<VkDescriptorSetLayout>(setLayout));
|
|
|
|
// don't have to handle separate vert/frag layouts as push constant ranges must be identical
|
|
const VulkanCreationInfo::PipelineLayout &pipeLayoutInfo =
|
|
m_pDriver->m_CreationInfo
|
|
.m_PipelineLayout[pipeInfo.ownLayout != ResourceId() ? pipeInfo.ownLayout
|
|
: pipeInfo.vertLayout];
|
|
const rdcarray<VkPushConstantRange> &push = pipeLayoutInfo.pushRanges;
|
|
|
|
VkPipelineLayoutCreateInfo pipeLayoutCreateInfo = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
|
NULL,
|
|
pipeLayoutInfo.flags,
|
|
(uint32_t)descSetLayouts.size(),
|
|
descSetLayouts.data(),
|
|
(uint32_t)push.size(),
|
|
push.data(),
|
|
};
|
|
|
|
VkResult vkr = m_pDriver->vkCreatePipelineLayout(m_pDriver->GetDev(), &pipeLayoutCreateInfo,
|
|
NULL, &m_CombinedPipeLayouts[pipeline]);
|
|
CHECK_VKR(m_pDriver, vkr);
|
|
|
|
ret.layout = m_CombinedPipeLayouts[pipeline];
|
|
}
|
|
}
|
|
|
|
static VkFormat colFormats[16] = {};
|
|
static VkPipelineRenderingCreateInfo dynRenderCreate = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO, NULL, pipeInfo.viewMask, 0, colFormats,
|
|
};
|
|
|
|
if(pipeInfo.renderpass == ResourceId())
|
|
{
|
|
dynRenderCreate.depthAttachmentFormat = pipeInfo.depthFormat;
|
|
dynRenderCreate.stencilAttachmentFormat = pipeInfo.stencilFormat;
|
|
dynRenderCreate.colorAttachmentCount = (uint32_t)pipeInfo.colorFormats.size();
|
|
memcpy(colFormats, pipeInfo.colorFormats.data(), pipeInfo.colorFormats.byteSize());
|
|
|
|
dynRenderCreate.pNext = ret.pNext;
|
|
ret.pNext = &dynRenderCreate;
|
|
}
|
|
|
|
static VkFormat customResColFormats[16] = {};
|
|
static VkCustomResolveCreateInfoEXT customResCreate = {
|
|
VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT, NULL, VK_FALSE, 0, customResColFormats};
|
|
|
|
if(pipeInfo.renderpass == ResourceId() && (pipeInfo.hasCustomResCreateInfo))
|
|
{
|
|
customResCreate.customResolve = pipeInfo.customResCreateInfo.customResolve;
|
|
customResCreate.colorAttachmentCount = (uint32_t)pipeInfo.customResCreateInfo.colorFormats.size();
|
|
memcpy(customResColFormats, pipeInfo.customResCreateInfo.colorFormats.data(),
|
|
pipeInfo.customResCreateInfo.colorFormats.byteSize());
|
|
customResCreate.depthAttachmentFormat = pipeInfo.customResCreateInfo.depthFormat;
|
|
customResCreate.stencilAttachmentFormat = pipeInfo.customResCreateInfo.stencilFormat;
|
|
customResCreate.pNext = ret.pNext;
|
|
ret.pNext = &customResCreate;
|
|
}
|
|
|
|
static VkPipelineDiscardRectangleStateCreateInfoEXT discardRects = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_DISCARD_RECTANGLE_STATE_CREATE_INFO_EXT,
|
|
};
|
|
|
|
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_EXT_discard_rectangles)
|
|
{
|
|
discardRects.discardRectangleMode = pipeInfo.discardMode;
|
|
discardRects.discardRectangleCount = (uint32_t)pipeInfo.discardRectangles.size();
|
|
discardRects.pDiscardRectangles = pipeInfo.discardRectangles.data();
|
|
|
|
discardRects.pNext = ret.pNext;
|
|
ret.pNext = &discardRects;
|
|
}
|
|
|
|
static VkPipelineFragmentShadingRateStateCreateInfoKHR shadingRate = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_SHADING_RATE_STATE_CREATE_INFO_KHR,
|
|
};
|
|
|
|
if(pipeInfo.shadingRate.width != 1 || pipeInfo.shadingRate.height != 1 ||
|
|
pipeInfo.shadingRateCombiners[0] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR ||
|
|
pipeInfo.shadingRateCombiners[1] != VK_FRAGMENT_SHADING_RATE_COMBINER_OP_KEEP_KHR)
|
|
{
|
|
shadingRate.fragmentSize = pipeInfo.shadingRate;
|
|
shadingRate.combinerOps[0] = pipeInfo.shadingRateCombiners[0];
|
|
shadingRate.combinerOps[1] = pipeInfo.shadingRateCombiners[1];
|
|
|
|
shadingRate.pNext = ret.pNext;
|
|
ret.pNext = &shadingRate;
|
|
}
|
|
|
|
static VkPipelineRasterizationProvokingVertexStateCreateInfoEXT provokeSetup = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_PROVOKING_VERTEX_STATE_CREATE_INFO_EXT,
|
|
};
|
|
|
|
if(m_pDriver->GetExtensions(GetRecord(m_Device)).ext_EXT_provoking_vertex)
|
|
{
|
|
provokeSetup.provokingVertexMode = pipeInfo.provokingVertex;
|
|
|
|
provokeSetup.pNext = rs.pNext;
|
|
rs.pNext = &provokeSetup;
|
|
}
|
|
|
|
uint64_t flags = pipeInfo.flags;
|
|
// never create derivatives
|
|
flags &= ~VK_PIPELINE_CREATE_DERIVATIVE_BIT;
|
|
|
|
flags &= ~VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
|
|
flags &= ~VK_PIPELINE_CREATE_RETAIN_LINK_TIME_OPTIMIZATION_INFO_BIT_EXT;
|
|
|
|
static VkPipelineCreateFlags2CreateInfo createFlags = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO,
|
|
};
|
|
|
|
if(pipeInfo.useCreateFlags2 && m_pDriver->Maintenance5())
|
|
{
|
|
createFlags.flags = flags;
|
|
|
|
createFlags.pNext = ret.pNext;
|
|
ret.pNext = &createFlags;
|
|
}
|
|
else
|
|
{
|
|
ret.flags = (uint32_t)flags;
|
|
}
|
|
|
|
static VkPipelineRobustnessCreateInfo VkPipelineRobustnessCreateInfo = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_ROBUSTNESS_CREATE_INFO,
|
|
};
|
|
|
|
// we only need to specify vertex input robustness, all per-shader robustness is handled via
|
|
// pNexts on the stages above
|
|
if(pipeInfo.vertexInputRobustness != VK_PIPELINE_ROBUSTNESS_BUFFER_BEHAVIOR_DEVICE_DEFAULT)
|
|
{
|
|
VkPipelineRobustnessCreateInfo.vertexInputs = pipeInfo.vertexInputRobustness;
|
|
|
|
VkPipelineRobustnessCreateInfo.pNext = ret.pNext;
|
|
ret.pNext = &VkPipelineRobustnessCreateInfo;
|
|
}
|
|
|
|
static VkPipelineFragmentDensityMapLayeredCreateInfoVALVE
|
|
VkPipelineFragmentDensityMapLayeredCreateInfoVALVE = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_FRAGMENT_DENSITY_MAP_LAYERED_CREATE_INFO_VALVE,
|
|
};
|
|
|
|
// we only need to specify vertex input robustness, all per-shader robustness is handled via
|
|
// pNexts on the stages above
|
|
if(pipeInfo.maxFragmentDensityMapLayers)
|
|
{
|
|
VkPipelineFragmentDensityMapLayeredCreateInfoVALVE.maxFragmentDensityMapLayers =
|
|
pipeInfo.maxFragmentDensityMapLayers;
|
|
|
|
VkPipelineFragmentDensityMapLayeredCreateInfoVALVE.pNext = ret.pNext;
|
|
ret.pNext = &VkPipelineFragmentDensityMapLayeredCreateInfoVALVE;
|
|
}
|
|
|
|
pipeCreateInfo = ret;
|
|
}
|
|
|
|
void VulkanShaderCache::MakeComputePipelineInfo(VkComputePipelineCreateInfo &pipeCreateInfo,
|
|
ResourceId pipeline)
|
|
{
|
|
const VulkanCreationInfo::Pipeline &pipeInfo = m_pDriver->m_CreationInfo.m_Pipeline[pipeline];
|
|
|
|
VulkanResourceManager *rm = m_pDriver->GetResourceManager();
|
|
|
|
VkPipelineShaderStageCreateInfo stage; // Returned by value
|
|
static VkSpecializationInfo specInfo;
|
|
static rdcarray<VkSpecializationMapEntry> specMapEntries;
|
|
|
|
// the specialization constants can't use more than a uint64_t, so we just over-allocate
|
|
static rdcarray<uint64_t> specdata;
|
|
|
|
const uint32_t i = 5; // Compute stage
|
|
RDCASSERT(pipeInfo.shaders[i].module != ResourceId());
|
|
|
|
size_t specEntries = pipeInfo.shaders[i].specialization.size();
|
|
|
|
specdata.resize(specEntries);
|
|
specMapEntries.resize(specEntries);
|
|
VkSpecializationMapEntry *entry = specMapEntries.data();
|
|
|
|
stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
stage.stage = (VkShaderStageFlagBits)(1 << i);
|
|
stage.module = rm->GetHandle<VkShaderModule>(pipeInfo.shaders[i].module);
|
|
stage.pName = pipeInfo.shaders[i].entryPoint.c_str();
|
|
stage.pNext = NULL;
|
|
stage.pSpecializationInfo = NULL;
|
|
stage.flags = pipeInfo.shaders[i].flags;
|
|
|
|
uint32_t dataOffset = 0;
|
|
|
|
if(!pipeInfo.shaders[i].specialization.empty())
|
|
{
|
|
stage.pSpecializationInfo = &specInfo;
|
|
specInfo.pMapEntries = entry;
|
|
specInfo.mapEntryCount = (uint32_t)pipeInfo.shaders[i].specialization.size();
|
|
|
|
for(size_t s = 0; s < pipeInfo.shaders[i].specialization.size(); s++)
|
|
{
|
|
entry[s].constantID = pipeInfo.shaders[i].specialization[s].specID;
|
|
entry[s].size = pipeInfo.shaders[i].specialization[s].dataSize;
|
|
entry[s].offset = dataOffset * sizeof(uint64_t);
|
|
|
|
specdata[dataOffset] = pipeInfo.shaders[i].specialization[s].value;
|
|
|
|
dataOffset++;
|
|
}
|
|
|
|
specInfo.dataSize = specdata.size() * sizeof(uint64_t);
|
|
specInfo.pData = specdata.data();
|
|
}
|
|
|
|
static VkPipelineShaderStageRequiredSubgroupSizeCreateInfo reqSubgroupSize = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_REQUIRED_SUBGROUP_SIZE_CREATE_INFO,
|
|
};
|
|
|
|
if(pipeInfo.shaders[i].requiredSubgroupSize != 0)
|
|
{
|
|
reqSubgroupSize.requiredSubgroupSize = pipeInfo.shaders[i].requiredSubgroupSize;
|
|
reqSubgroupSize.pNext = (void *)stage.pNext;
|
|
stage.pNext = &reqSubgroupSize;
|
|
}
|
|
|
|
static VkPipelineRobustnessCreateInfo robustness = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_ROBUSTNESS_CREATE_INFO,
|
|
};
|
|
|
|
if(pipeInfo.shaders[i].HasRobustness())
|
|
{
|
|
robustness.images = pipeInfo.shaders[i].imageRobustness;
|
|
robustness.uniformBuffers = pipeInfo.shaders[i].uniformBufferRobustness;
|
|
robustness.storageBuffers = pipeInfo.shaders[i].storageBufferRobustness;
|
|
robustness.pNext = stage.pNext;
|
|
stage.pNext = &robustness;
|
|
}
|
|
|
|
VkComputePipelineCreateInfo ret = {
|
|
VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO,
|
|
NULL,
|
|
0,
|
|
stage,
|
|
rm->GetHandle<VkPipelineLayout>(pipeInfo.compLayout),
|
|
VK_NULL_HANDLE, // base pipeline handle
|
|
0, // base pipeline index
|
|
};
|
|
|
|
uint64_t flags = pipeInfo.flags;
|
|
// never create derivatives
|
|
flags &= ~VK_PIPELINE_CREATE_DERIVATIVE_BIT;
|
|
|
|
static VkPipelineCreateFlags2CreateInfo createFlags = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO,
|
|
};
|
|
|
|
if(pipeInfo.useCreateFlags2 && m_pDriver->Maintenance5())
|
|
{
|
|
createFlags.flags = flags;
|
|
|
|
createFlags.pNext = ret.pNext;
|
|
ret.pNext = &createFlags;
|
|
}
|
|
else
|
|
{
|
|
ret.flags = (uint32_t)flags;
|
|
}
|
|
|
|
pipeCreateInfo = ret;
|
|
}
|
|
|
|
void VulkanShaderCache::MakeShaderObjectInfo(VkShaderCreateInfoEXT &shadCreateInfo,
|
|
ResourceId shaderObj)
|
|
{
|
|
// skip if invalid shader object
|
|
if(shaderObj == ResourceId())
|
|
{
|
|
RDCEraseEl(shadCreateInfo);
|
|
return;
|
|
}
|
|
|
|
const VulkanCreationInfo::ShaderObject &shadInfo =
|
|
m_pDriver->m_CreationInfo.m_ShaderObject[shaderObj];
|
|
|
|
const VulkanCreationInfo::ShaderModule &moduleInfo =
|
|
m_pDriver->m_CreationInfo.m_ShaderModule[shadInfo.shad.module];
|
|
const rdcarray<uint32_t> &modSpirv = moduleInfo.spirv.GetSPIRV();
|
|
|
|
VulkanResourceManager *rm = m_pDriver->GetResourceManager();
|
|
|
|
static VkSpecializationInfo specInfo;
|
|
static rdcarray<VkSpecializationMapEntry> specMapEntries;
|
|
|
|
// the specialization constants can't use more than a uint64_t, so we just over-allocate
|
|
static rdcarray<uint64_t> specdata;
|
|
|
|
size_t specEntries = shadInfo.shad.specialization.size();
|
|
|
|
specdata.resize(specEntries);
|
|
specMapEntries.resize(specEntries);
|
|
VkSpecializationMapEntry *entry = specMapEntries.data();
|
|
|
|
uint32_t stage = (uint32_t)shadInfo.shad.stage;
|
|
|
|
static rdcarray<VkDescriptorSetLayout> descSetLayouts;
|
|
descSetLayouts = {};
|
|
|
|
for(ResourceId setLayout : shadInfo.descSetLayouts)
|
|
descSetLayouts.push_back(rm->GetHandle<VkDescriptorSetLayout>(setLayout));
|
|
|
|
VkShaderCreateInfoEXT ret = {VK_STRUCTURE_TYPE_SHADER_CREATE_INFO_EXT,
|
|
NULL,
|
|
shadInfo.flags,
|
|
(VkShaderStageFlagBits)(1 << stage),
|
|
shadInfo.nextStage,
|
|
VK_SHADER_CODE_TYPE_SPIRV_EXT,
|
|
modSpirv.size() * sizeof(uint32_t),
|
|
modSpirv.data(),
|
|
shadInfo.shad.entryPoint.c_str(),
|
|
(uint32_t)descSetLayouts.size(),
|
|
descSetLayouts.data(),
|
|
(uint32_t)shadInfo.pushRanges.size(),
|
|
shadInfo.pushRanges.data(),
|
|
NULL};
|
|
|
|
// specialization info
|
|
uint32_t dataOffset = 0;
|
|
|
|
if(!shadInfo.shad.specialization.empty())
|
|
{
|
|
ret.pSpecializationInfo = &specInfo;
|
|
specInfo.pMapEntries = entry;
|
|
specInfo.mapEntryCount = (uint32_t)shadInfo.shad.specialization.size();
|
|
|
|
for(size_t s = 0; s < shadInfo.shad.specialization.size(); s++)
|
|
{
|
|
entry[s].constantID = shadInfo.shad.specialization[s].specID;
|
|
entry[s].size = shadInfo.shad.specialization[s].dataSize;
|
|
entry[s].offset = dataOffset * sizeof(uint64_t);
|
|
|
|
specdata[dataOffset] = shadInfo.shad.specialization[s].value;
|
|
|
|
dataOffset++;
|
|
}
|
|
|
|
specInfo.dataSize = specdata.size() * sizeof(uint64_t);
|
|
specInfo.pData = specdata.data();
|
|
}
|
|
|
|
static VkCustomResolveCreateInfoEXT customResCreate = {
|
|
VK_STRUCTURE_TYPE_CUSTOM_RESOLVE_CREATE_INFO_EXT,
|
|
NULL,
|
|
VK_FALSE,
|
|
0,
|
|
NULL,
|
|
VK_FORMAT_UNDEFINED,
|
|
VK_FORMAT_UNDEFINED,
|
|
};
|
|
if(shadInfo.hasCustomResCreateInfo)
|
|
{
|
|
customResCreate.customResolve = shadInfo.customResolve;
|
|
customResCreate.pNext = ret.pNext;
|
|
ret.pNext = &customResCreate;
|
|
}
|
|
shadCreateInfo = ret;
|
|
}
|