17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-26 18:14:55 +01:00
Files
maszyna/rendering/vulkan14/vulkan14shaders.cpp

783 lines
27 KiB
C++

/*
This Source Code Form is subject to the
terms of the Mozilla Public License, v.
2.0. If a copy of the MPL was not
distributed with this file, You can
obtain one at
http://mozilla.org/MPL/2.0/.
*/
#include "stdafx.h"
#include "rendering/vulkan14/vulkan14shaders.h"
#include "rendering/vulkan14/vulkan14device.h"
#include "rendering/vulkan14/vulkan14bindings.h"
#include "gl/ubo.h"
#include "utilities/Globals.h"
#include "utilities/Logs.h"
#include "utilities/utilities.h"
#include <glslang/Public/ResourceLimits.h>
#include <glslang/Public/ShaderLang.h>
#include <SPIRV/GlslangToSpv.h>
#include <chrono>
#include <cstring>
#include <filesystem>
#include <fstream>
#include <sstream>
namespace vk14
{
namespace
{
EShLanguage language_of(gfx::shader_stage const Stage)
{
switch (Stage)
{
case gfx::shader_stage::vertex:
return EShLangVertex;
case gfx::shader_stage::fragment:
return EShLangFragment;
case gfx::shader_stage::geometry:
return EShLangGeometry;
case gfx::shader_stage::compute:
return EShLangCompute;
}
return EShLangVertex;
}
VkShaderStageFlagBits stage_bit(gfx::shader_stage const Stage)
{
switch (Stage)
{
case gfx::shader_stage::vertex:
return VK_SHADER_STAGE_VERTEX_BIT;
case gfx::shader_stage::fragment:
return VK_SHADER_STAGE_FRAGMENT_BIT;
case gfx::shader_stage::geometry:
return VK_SHADER_STAGE_GEOMETRY_BIT;
case gfx::shader_stage::compute:
return VK_SHADER_STAGE_COMPUTE_BIT;
}
return VK_SHADER_STAGE_VERTEX_BIT;
}
std::string files_of(gfx::shader_source const &Source)
{
std::string result;
for (std::size_t i = 0; i < Source.files.size(); ++i)
{
result += (i > 0 ? ", " : "") + std::to_string(i) + ": " + Source.files[i];
}
return result;
}
std::vector<std::uint32_t> read_spirv(std::string const &Path)
{
std::ifstream file(Path, std::ios::binary | std::ios::ate);
if (false == file.is_open())
{
return {};
}
auto const size{static_cast<std::size_t>(file.tellg())};
if ((size < 20) || (size % 4 != 0))
{
return {};
}
std::vector<std::uint32_t> words(size / 4);
file.seekg(0);
file.read(reinterpret_cast<char *>(words.data()), static_cast<std::streamsize>(size));
if ((false == static_cast<bool>(file)) || (words[0] != 0x07230203u))
{
return {};
}
return words;
}
// written beside and moved into place, so a program reading the cache at the same time doesn't get half of the file
void write_file(std::string const &Path, void const *Data, std::size_t const Size)
{
std::error_code error;
std::filesystem::create_directories(std::filesystem::path(Path).parent_path(), error);
auto const temporary{Path + ".tmp"};
{
std::ofstream file(temporary, std::ios::binary | std::ios::trunc);
if (false == file.is_open())
{
return;
}
file.write(static_cast<char const *>(Data), static_cast<std::streamsize>(Size));
if (false == static_cast<bool>(file))
{
return;
}
}
std::filesystem::rename(temporary, Path, error);
if (error)
{
std::filesystem::remove(temporary, error);
}
}
std::string hex(std::uint64_t const Value)
{
char buffer[17] = {};
std::snprintf(buffer, sizeof(buffer), "%016llx", static_cast<unsigned long long>(Value));
return buffer;
}
// fnv-1a 64 over more data
void hash_bytes(std::uint64_t &Hash, void const *Data, std::size_t const Size)
{
auto const *bytes{static_cast<std::uint8_t const *>(Data)};
for (std::size_t i = 0; i < Size; ++i)
{
Hash ^= bytes[i];
Hash *= 1099511628211ull;
}
}
template <typename Type_> void hash_value(std::uint64_t &Hash, Type_ const &Value)
{
hash_bytes(Hash, &Value, sizeof(Value));
}
std::string user_cache_dir(std::string const &Name)
{
// with a separator at the end
auto const path{user_config_path("cache")};
auto const name{Name.empty() ? std::string{} : Name + "/"};
if (path.empty())
{
return "cache/vulkan14/" + name;
}
return (path / "vulkan14").string() + "/" + name;
}
} // namespace
// shader library
shader_library::~shader_library()
{
shutdown();
}
bool shader_library::init(std::string const &Preamble, std::string &Reason)
{
m_preamble = Preamble;
if (false == glslang::InitializeProcess())
{
Reason = "the shader compiler (glslang) can't start";
return false;
}
m_glslang = true;
auto const version{glslang::GetVersion()};
m_compilerversion = "glslang " + std::to_string(version.major) + "." + std::to_string(version.minor) + "." + std::to_string(version.patch) + " vulkan1.4 spirv1.6";
m_cachedir = user_cache_dir("shaders");
m_binaries = Global.gfx_shader_binaries;
if (m_binaries)
{
// the binaries are compiled with the constants written in the files, the sources get those of the renderer
std::vector<std::string> differences;
if (false == gfx::shader_constants_match(gfx::shader_api::vulkan14, "common.glsl", Preamble, {}, differences))
{
std::string text;
for (auto const &difference : differences)
{
text += "\n " + difference;
}
ErrorLog("vulkan: the constants in " + gfx::shader_source_dir(gfx::shader_api::vulkan14) + "common.glsl aren't those of the renderer, so the SPIR-V binaries of " +
gfx::shader_binary_dir(gfx::shader_api::vulkan14) + " can't be used; the shaders are compiled from the sources:" + text,
logtype::shader);
m_binaries = false;
}
}
log_info("shaders: " + std::string(m_binaries ? "the SPIR-V binaries of " + gfx::shader_binary_dir(gfx::shader_api::vulkan14) + " first, then " : "") + "the cache in " + m_cachedir + ", then " + m_compilerversion);
return true;
}
void shader_library::shutdown()
{
m_shaders.clear();
if (m_glslang)
{
glslang::FinalizeProcess();
m_glslang = false;
}
}
std::shared_ptr<shader_code const> shader_library::get(std::string const &Name, std::string const &Defines)
{
auto const key{Name + "|" + Defines};
auto const lookup{m_shaders.find(key)};
if (lookup != m_shaders.end())
{
return lookup->second;
}
auto code{std::make_shared<shader_code>()};
code->source = gfx::preprocess_shader(gfx::shader_api::vulkan14, Name, Defines, m_preamble, true);
code->stage = stage_bit(code->source.stage);
auto const label{Name + (code->source.variant.empty() ? "" : " (" + code->source.variant + ")")};
if (m_binaries)
{
std::string reason;
code->spirv = gfx::load_shader_binary(code->source, reason);
if (false == reason.empty())
{
log_info("shader " + label + ": " + reason + ", compiled from the source");
}
if (false == code->spirv.empty())
{
code->origin = "binary";
++m_frombinary;
}
}
if (code->spirv.empty())
{
if (false == code->source.has_source)
{
throw gfx::shader_exception("cannot read shader: " + gfx::shader_source_dir(gfx::shader_api::vulkan14) + Name);
}
std::uint64_t hash{14695981039346656037ull};
hash_bytes(hash, code->source.text.data(), code->source.text.size());
hash_bytes(hash, m_compilerversion.data(), m_compilerversion.size());
auto const cachepath{m_cachedir + Name + (code->source.variant.empty() ? "" : "." + code->source.variant) + "." + hex(hash) + ".spv"};
code->spirv = read_spirv(cachepath);
if (false == code->spirv.empty())
{
code->origin = "cache";
++m_fromcache;
}
else
{
code->spirv = compile(code->source);
code->origin = "compiled";
++m_compiled;
write_file(cachepath, code->spirv.data(), code->spirv.size() * sizeof(std::uint32_t));
}
}
// the text isn't needed any more
code->source.text = std::string();
m_shaders.emplace(key, code);
return code;
}
std::vector<std::uint32_t> shader_library::compile(gfx::shader_source const &Source) const
{
auto const language{language_of(Source.stage)};
glslang::TShader shader(language);
char const *text{Source.text.c_str()};
char const *name{Source.name.c_str()};
shader.setStringsWithLengthsAndNames(&text, nullptr, &name, 1);
shader.setEnvInput(glslang::EShSourceGlsl, language, glslang::EShClientVulkan, 100);
shader.setEnvClient(glslang::EShClientVulkan, glslang::EShTargetVulkan_1_4);
shader.setEnvTarget(glslang::EShTargetSpv, glslang::EShTargetSpv_1_6);
auto const messages{static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules)};
if (false == shader.parse(GetDefaultResources(), 460, false, messages))
{
auto const info{std::string(shader.getInfoLog())};
ErrorLog("bad shader: " + Source.name + ": " + info + "(source strings: " + files_of(Source) + ")", logtype::shader);
throw gfx::shader_exception("failed to compile " + Source.name + ": " + info);
}
glslang::TProgram program;
program.addShader(&shader);
if (false == program.link(messages))
{
auto const info{std::string(program.getInfoLog())};
ErrorLog("bad shader: " + Source.name + ": " + info, logtype::shader);
throw gfx::shader_exception("failed to link " + Source.name + ": " + info);
}
std::vector<std::uint32_t> spirv;
glslang::SpvOptions options;
options.validate = false;
glslang::GlslangToSpv(*program.getIntermediate(language), spirv, &options);
if (spirv.empty())
{
throw gfx::shader_exception("no SPIR-V made of " + Source.name);
}
return spirv;
}
std::string shader_library::info() const
{
return "shaders: " + std::to_string(m_shaders.size()) + " (" + std::to_string(m_frombinary) + " binaries, " + std::to_string(m_fromcache) + " from the cache, " + std::to_string(m_compiled) + " compiled)";
}
// pipeline library
pipeline_library::~pipeline_library()
{
shutdown();
}
bool pipeline_library::init(device const &Device, std::string &Reason)
{
m_device = &Device;
auto const cachedir{user_cache_dir("")};
m_cachepath = cachedir + "pipelines.bin";
// the cache of another device or driver is no good; vulkan would ignore it, but it would be written over anyway
std::vector<char> data;
{
std::ifstream file(m_cachepath, std::ios::binary | std::ios::ate);
if (file.is_open())
{
data.resize(static_cast<std::size_t>(file.tellg()));
file.seekg(0);
file.read(data.data(), static_cast<std::streamsize>(data.size()));
if (false == static_cast<bool>(file))
{
data.clear();
}
}
}
if (false == data.empty())
{
VkPipelineCacheHeaderVersionOne header{};
auto const &caps{Device.caps()};
bool valid{data.size() >= sizeof(header)};
if (valid)
{
std::memcpy(&header, data.data(), sizeof(header));
valid = (header.headerSize >= sizeof(header)) && (header.headerVersion == VK_PIPELINE_CACHE_HEADER_VERSION_ONE) && (header.vendorID == caps.vendor_id) && (header.deviceID == caps.device_id) &&
(std::memcmp(header.pipelineCacheUUID, caps.pipeline_cache_uuid.data(), VK_UUID_SIZE) == 0);
}
if (false == valid)
{
log_info("the pipeline cache " + m_cachepath + " comes from another device or driver, made anew");
data.clear();
}
}
VkPipelineCacheCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO;
info.initialDataSize = data.size();
info.pInitialData = (data.empty() ? nullptr : data.data());
if (!VK14_VERIFY(vkCreatePipelineCache(Device.handle(), &info, nullptr, &m_cache)))
{
// without the old data, then
info.initialDataSize = 0;
info.pInitialData = nullptr;
if (!VK14_VERIFY(vkCreatePipelineCache(Device.handle(), &info, nullptr, &m_cache)))
{
m_cache = VK_NULL_HANDLE;
Reason = "no pipeline cache";
return false;
}
}
log_info("pipeline cache " + m_cachepath + (data.empty() ? " (empty)" : " (" + std::to_string(data.size() / 1024) + " KiB)"));
return true;
}
void pipeline_library::shutdown()
{
if (m_device == nullptr)
{
return;
}
auto const device{m_device->handle()};
if (device != VK_NULL_HANDLE)
{
for (auto const &[key, pipeline] : m_pipelines)
{
if (pipeline != VK_NULL_HANDLE)
{
vkDestroyPipeline(device, pipeline, nullptr);
}
}
if (m_cache != VK_NULL_HANDLE)
{
std::size_t size{0};
if (VK14_VERIFY(vkGetPipelineCacheData(device, m_cache, &size, nullptr)) && (size > 0))
{
std::vector<char> data(size);
if (VK14_VERIFY(vkGetPipelineCacheData(device, m_cache, &size, data.data())))
{
write_file(m_cachepath, data.data(), size);
}
}
vkDestroyPipelineCache(device, m_cache, nullptr);
}
}
m_pipelines.clear();
m_cache = VK_NULL_HANDLE;
m_device = nullptr;
}
std::uint64_t pipeline_library::key_of(pipeline_desc const &Desc) const
{
std::uint64_t hash{14695981039346656037ull};
for (auto const &stage : Desc.stages)
{
hash_value(hash, stage.get());
}
hash_value(hash, Desc.layout);
for (auto const &binding : Desc.vertices.bindings)
{
hash_value(hash, binding.binding);
hash_value(hash, binding.stride);
hash_value(hash, binding.inputRate);
}
for (auto const &attribute : Desc.vertices.attributes)
{
hash_value(hash, attribute.location);
hash_value(hash, attribute.binding);
hash_value(hash, attribute.format);
hash_value(hash, attribute.offset);
}
hash_value(hash, Desc.topology);
hash_value(hash, Desc.blending);
for (auto const format : Desc.colour_formats)
{
hash_value(hash, format);
}
hash_value(hash, Desc.depth_format);
hash_value(hash, Desc.samples);
for (auto const value : Desc.specialization)
{
hash_value(hash, value);
}
return hash;
}
namespace
{
VkPrimitiveTopology topology_class_head(VkPrimitiveTopology const Topology)
{
return Topology;
}
std::vector<VkDynamicState> const dynamic_states{VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
VK_DYNAMIC_STATE_CULL_MODE,
VK_DYNAMIC_STATE_FRONT_FACE,
VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY,
VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE,
VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE,
VK_DYNAMIC_STATE_DEPTH_COMPARE_OP,
VK_DYNAMIC_STATE_DEPTH_BIAS_ENABLE,
VK_DYNAMIC_STATE_DEPTH_BIAS,
VK_DYNAMIC_STATE_PRIMITIVE_RESTART_ENABLE,
VK_DYNAMIC_STATE_STENCIL_TEST_ENABLE};
} // namespace
VkPipeline pipeline_library::get(pipeline_desc const &Desc)
{
auto const key{key_of(Desc)};
auto const lookup{m_pipelines.find(key)};
if (lookup != m_pipelines.end())
{
return lookup->second;
}
auto const start{std::chrono::steady_clock::now()};
// the code goes straight to the stages (maintenance5), with the specialization of all of them
std::vector<VkSpecializationMapEntry> entries(Desc.specialization.size());
for (std::size_t i = 0; i < entries.size(); ++i)
{
entries[i] = {static_cast<std::uint32_t>(i), static_cast<std::uint32_t>(i * sizeof(std::uint32_t)), sizeof(std::uint32_t)};
}
VkSpecializationInfo specialization{};
specialization.mapEntryCount = static_cast<std::uint32_t>(entries.size());
specialization.pMapEntries = entries.data();
specialization.dataSize = Desc.specialization.size() * sizeof(std::uint32_t);
specialization.pData = Desc.specialization.data();
std::vector<VkShaderModuleCreateInfo> modules(Desc.stages.size());
std::vector<VkPipelineShaderStageCreateInfo> stages(Desc.stages.size());
for (std::size_t i = 0; i < Desc.stages.size(); ++i)
{
modules[i].sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
modules[i].codeSize = Desc.stages[i]->spirv.size() * sizeof(std::uint32_t);
modules[i].pCode = Desc.stages[i]->spirv.data();
stages[i].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
stages[i].pNext = &modules[i];
stages[i].stage = Desc.stages[i]->stage;
stages[i].module = VK_NULL_HANDLE;
stages[i].pName = "main";
stages[i].pSpecializationInfo = (entries.empty() ? nullptr : &specialization);
}
VkPipelineVertexInputStateCreateInfo vertexinput{};
vertexinput.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vertexinput.vertexBindingDescriptionCount = static_cast<std::uint32_t>(Desc.vertices.bindings.size());
vertexinput.pVertexBindingDescriptions = Desc.vertices.bindings.data();
vertexinput.vertexAttributeDescriptionCount = static_cast<std::uint32_t>(Desc.vertices.attributes.size());
vertexinput.pVertexAttributeDescriptions = Desc.vertices.attributes.data();
VkPipelineInputAssemblyStateCreateInfo inputassembly{};
inputassembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
inputassembly.topology = topology_class_head(Desc.topology);
VkPipelineViewportStateCreateInfo viewport{};
viewport.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
viewport.viewportCount = 1;
viewport.scissorCount = 1;
VkPipelineRasterizationStateCreateInfo rasterization{};
rasterization.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rasterization.polygonMode = VK_POLYGON_MODE_FILL;
rasterization.cullMode = VK_CULL_MODE_NONE;
rasterization.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
rasterization.lineWidth = 1.f;
VkPipelineMultisampleStateCreateInfo multisample{};
multisample.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
multisample.rasterizationSamples = Desc.samples;
VkPipelineDepthStencilStateCreateInfo depthstencil{};
depthstencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
depthstencil.depthCompareOp = VK_COMPARE_OP_GREATER_OR_EQUAL;
depthstencil.maxDepthBounds = 1.f;
std::vector<VkPipelineColorBlendAttachmentState> attachments(Desc.colour_formats.size());
for (auto &attachment : attachments)
{
attachment.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
switch (Desc.blending)
{
case blend_mode::none:
break;
case blend_mode::alpha:
attachment.blendEnable = VK_TRUE;
attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
attachment.colorBlendOp = VK_BLEND_OP_ADD;
attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
break;
case blend_mode::premultiplied:
attachment.blendEnable = VK_TRUE;
attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
attachment.colorBlendOp = VK_BLEND_OP_ADD;
attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
break;
case blend_mode::scene_alpha:
attachment.blendEnable = VK_TRUE;
attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
attachment.colorBlendOp = VK_BLEND_OP_ADD;
attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA;
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
break;
case blend_mode::scene_additive:
attachment.blendEnable = VK_TRUE;
attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
attachment.colorBlendOp = VK_BLEND_OP_ADD;
attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA;
attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
break;
case blend_mode::additive:
attachment.blendEnable = VK_TRUE;
attachment.srcColorBlendFactor = VK_BLEND_FACTOR_ONE;
attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE;
attachment.colorBlendOp = VK_BLEND_OP_ADD;
attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE;
attachment.alphaBlendOp = VK_BLEND_OP_ADD;
break;
}
}
VkPipelineColorBlendStateCreateInfo blend{};
blend.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
blend.attachmentCount = static_cast<std::uint32_t>(attachments.size());
blend.pAttachments = attachments.data();
VkPipelineDynamicStateCreateInfo dynamic{};
dynamic.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamic.dynamicStateCount = static_cast<std::uint32_t>(dynamic_states.size());
dynamic.pDynamicStates = dynamic_states.data();
// no render pass: the formats of the attachments only (dynamic rendering)
VkPipelineRenderingCreateInfo rendering{};
rendering.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO;
rendering.colorAttachmentCount = static_cast<std::uint32_t>(Desc.colour_formats.size());
rendering.pColorAttachmentFormats = Desc.colour_formats.data();
rendering.depthAttachmentFormat = Desc.depth_format;
VkGraphicsPipelineCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
info.pNext = &rendering;
info.stageCount = static_cast<std::uint32_t>(stages.size());
info.pStages = stages.data();
info.pVertexInputState = &vertexinput;
info.pInputAssemblyState = &inputassembly;
info.pViewportState = &viewport;
info.pRasterizationState = &rasterization;
info.pMultisampleState = &multisample;
info.pDepthStencilState = &depthstencil;
info.pColorBlendState = &blend;
info.pDynamicState = &dynamic;
info.layout = Desc.layout;
VkPipeline pipeline{VK_NULL_HANDLE};
if (!VK14_VERIFY(vkCreateGraphicsPipelines(m_device->handle(), m_cache, 1, &info, nullptr, &pipeline)))
{
log_error("the pipeline " + Desc.name + " can't be made");
pipeline = VK_NULL_HANDLE;
}
else
{
name_object(m_device->handle(), VK_OBJECT_TYPE_PIPELINE, handle_value(pipeline), Desc.name);
}
m_creationms += std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count();
m_pipelines.emplace(key, pipeline);
return pipeline;
}
VkPipeline pipeline_library::compute(std::shared_ptr<shader_code const> const &Shader, VkPipelineLayout Layout, std::vector<std::uint32_t> const &Specialization, std::string const &Name)
{
std::uint64_t key{14695981039346656037ull};
hash_value(key, Shader.get());
hash_value(key, Layout);
for (auto const value : Specialization)
{
hash_value(key, value);
}
auto const lookup{m_pipelines.find(key)};
if (lookup != m_pipelines.end())
{
return lookup->second;
}
auto const start{std::chrono::steady_clock::now()};
std::vector<VkSpecializationMapEntry> entries(Specialization.size());
for (std::size_t i = 0; i < entries.size(); ++i)
{
entries[i] = {static_cast<std::uint32_t>(i), static_cast<std::uint32_t>(i * sizeof(std::uint32_t)), sizeof(std::uint32_t)};
}
VkSpecializationInfo specialization{};
specialization.mapEntryCount = static_cast<std::uint32_t>(entries.size());
specialization.pMapEntries = entries.data();
specialization.dataSize = Specialization.size() * sizeof(std::uint32_t);
specialization.pData = Specialization.data();
VkShaderModuleCreateInfo module{};
module.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
module.codeSize = Shader->spirv.size() * sizeof(std::uint32_t);
module.pCode = Shader->spirv.data();
VkComputePipelineCreateInfo info{};
info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
info.stage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
info.stage.pNext = &module;
info.stage.stage = VK_SHADER_STAGE_COMPUTE_BIT;
info.stage.pName = "main";
info.stage.pSpecializationInfo = (entries.empty() ? nullptr : &specialization);
info.layout = Layout;
VkPipeline pipeline{VK_NULL_HANDLE};
if (!VK14_VERIFY(vkCreateComputePipelines(m_device->handle(), m_cache, 1, &info, nullptr, &pipeline)))
{
log_error("the compute pipeline " + Name + " can't be made");
pipeline = VK_NULL_HANDLE;
}
else
{
name_object(m_device->handle(), VK_OBJECT_TYPE_PIPELINE, handle_value(pipeline), Name);
}
m_creationms += std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count();
m_pipelines.emplace(key, pipeline);
return pipeline;
}
std::string pipeline_library::info() const
{
char buffer[32] = {};
std::snprintf(buffer, sizeof(buffer), "%.1f", m_creationms);
return "pipelines: " + std::to_string(m_pipelines.size()) + " (made in " + buffer + " ms)";
}
void set_draw_state(VkCommandBuffer Commands, VkExtent2D Extent)
{
VkViewport viewport{0.f, 0.f, static_cast<float>(Extent.width), static_cast<float>(Extent.height), 0.f, 1.f};
vkCmdSetViewport(Commands, 0, 1, &viewport);
VkRect2D scissor{{0, 0}, Extent};
vkCmdSetScissor(Commands, 0, 1, &scissor);
vkCmdSetCullMode(Commands, VK_CULL_MODE_NONE);
vkCmdSetFrontFace(Commands, VK_FRONT_FACE_COUNTER_CLOCKWISE);
vkCmdSetPrimitiveTopology(Commands, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
vkCmdSetDepthTestEnable(Commands, VK_FALSE);
vkCmdSetDepthWriteEnable(Commands, VK_FALSE);
// reverse z, as the opengl renderer
vkCmdSetDepthCompareOp(Commands, VK_COMPARE_OP_GREATER_OR_EQUAL);
vkCmdSetDepthBiasEnable(Commands, VK_FALSE);
vkCmdSetDepthBias(Commands, 0.f, 0.f, 0.f);
vkCmdSetPrimitiveRestartEnable(Commands, VK_FALSE);
vkCmdSetStencilTestEnable(Commands, VK_FALSE);
}
} // namespace vk14
namespace vk14
{
std::string binding_preamble()
{
std::string preamble{"#define MASZYNA_CONSTANTS 1\n"};
auto const define = [&preamble](char const *Name, auto const Value, char const *Suffix = "") { preamble += std::string("#define ") + Name + " " + std::to_string(Value) + Suffix + "\n"; };
using namespace vk14::bindings;
define("UI_SET", UI_SET);
define("UI_TEXTURE", UI_TEXTURE);
define("PASS_SET", PASS_SET);
define("MATERIAL_SET", MATERIAL_SET);
define("SCENE_UBO", SCENE_UBO);
define("DRAW_SSBO", DRAW_SSBO);
define("INSTANCE_SSBO", INSTANCE_SSBO);
define("LIGHT_SSBO", LIGHT_SSBO);
define("CLUSTER_UBO", CLUSTER_UBO);
define("CLUSTER_RANGE_SSBO", CLUSTER_RANGE_SSBO);
define("CLUSTER_INDEX_SSBO", CLUSTER_INDEX_SSBO);
define("SHADOW_TEX", SHADOW_TEX);
define("ENV_TEX", ENV_TEX);
define("HEADLIGHT_TEX", HEADLIGHT_TEX);
define("PASS_BINDING_COUNT", PASS_BINDING_COUNT);
define("MATERIAL_TEXTURE_FIRST", MATERIAL_TEXTURE_FIRST);
define("CL_CLUSTER_UBO", CL_CLUSTER_UBO);
define("CL_AABB_SSBO", CL_AABB_SSBO);
define("CL_RANGE_SSBO", CL_RANGE_SSBO);
define("CL_INDEX_SSBO", CL_INDEX_SSBO);
define("CL_COUNTER_SSBO", CL_COUNTER_SSBO);
define("CL_LIGHT_SSBO", CL_LIGHT_SSBO);
define("POST_SET", POST_SET);
define("POST_TEXTURE_FIRST", POST_TEXTURE_FIRST);
define("PUSH_CONSTANTS_SIZE", PUSH_CONSTANTS_SIZE);
define("SPEC_OUTPUT_GAMMA", SPEC_OUTPUT_GAMMA);
define("SPEC_SHADOWMAP_ENABLED", SPEC_SHADOWMAP_ENABLED);
define("SPEC_ENVMAP_ENABLED", SPEC_ENVMAP_ENABLED);
define("SPEC_MOTIONBLUR_ENABLED", SPEC_MOTIONBLUR_ENABLED);
define("SPEC_POSTFX_ENABLED", SPEC_POSTFX_ENABLED);
define("SPEC_EXTRAEFFECTS_ENABLED", SPEC_EXTRAEFFECTS_ENABLED);
define("SPEC_COUNT", SPEC_COUNT);
// the constants of the game the blocks of the shaders are sized by, the same as for the opengl renderer (gl/ubo.h)
define("MAX_LIGHTS", static_cast<unsigned int>(gl::MAX_LIGHTS), "U");
define("MAX_CASCADES", static_cast<unsigned int>(gl::MAX_CASCADES), "U");
define("MAX_PARAMS", static_cast<unsigned int>(gl::MAX_PARAMS), "U");
define("MAX_HEADLIGHT_SHADOWS", static_cast<unsigned int>(gl::MAX_HEADLIGHT_SHADOWS), "U");
define("CLUSTER_GRID_X", gl::CLUSTER_GRID_X);
define("CLUSTER_GRID_Y", gl::CLUSTER_GRID_Y);
define("CLUSTER_GRID_Z", gl::CLUSTER_GRID_Z);
define("CLUSTER_COUNT", gl::CLUSTER_COUNT);
define("MAX_LIGHTS_PER_CLUSTER", gl::MAX_LIGHTS_PER_CLUSTER);
define("CLUSTER_BUILD_GROUP_SIZE", gl::CLUSTER_BUILD_GROUP_SIZE);
define("CLUSTER_CULL_GROUP_SIZE", gl::CLUSTER_CULL_GROUP_SIZE);
return preamble;
}
} // namespace vk14