WIP: Vulkan: Reuse shader modules. #122044
@ -232,6 +232,7 @@ set(VULKAN_SRC
|
||||
vulkan/vk_shader.cc
|
||||
vulkan/vk_shader_interface.cc
|
||||
vulkan/vk_shader_log.cc
|
||||
vulkan/vk_shader_modules.cc
|
||||
vulkan/vk_staging_buffer.cc
|
||||
vulkan/vk_state_manager.cc
|
||||
vulkan/vk_storage_buffer.cc
|
||||
@ -299,6 +300,7 @@ set(VULKAN_SRC
|
||||
vulkan/vk_shader.hh
|
||||
vulkan/vk_shader_interface.hh
|
||||
vulkan/vk_shader_log.hh
|
||||
vulkan/vk_shader_modules.hh
|
||||
vulkan/vk_staging_buffer.hh
|
||||
vulkan/vk_state_manager.hh
|
||||
vulkan/vk_storage_buffer.hh
|
||||
|
@ -49,6 +49,7 @@ void VKDevice::deinit()
|
||||
samplers_.free();
|
||||
destroy_discarded_resources();
|
||||
pipelines.free_data();
|
||||
shader_modules.free_data();
|
||||
vkDestroyPipelineCache(vk_device_, vk_pipeline_cache_, vk_allocation_callbacks);
|
||||
descriptor_set_layouts_.deinit();
|
||||
vmaDestroyAllocator(mem_allocator_);
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "vk_descriptor_set_layouts.hh"
|
||||
#include "vk_pipeline_pool.hh"
|
||||
#include "vk_samplers.hh"
|
||||
#include "vk_shader_modules.hh"
|
||||
#include "vk_timeline_semaphore.hh"
|
||||
|
||||
namespace blender::gpu {
|
||||
@ -112,6 +113,7 @@ class VKDevice : public NonCopyable {
|
||||
|
||||
public:
|
||||
render_graph::VKResourceStateTracker resources;
|
||||
VKShaderModules shader_modules;
|
||||
VKPipelinePool pipelines;
|
||||
|
||||
/**
|
||||
|
@ -568,24 +568,29 @@ VKShader::VKShader(const char *name) : Shader(name)
|
||||
context_ = VKContext::get();
|
||||
}
|
||||
|
||||
void VKShader::init(const shader::ShaderCreateInfo &info)
|
||||
{
|
||||
create_info_ = &info;
|
||||
}
|
||||
|
||||
VKShader::~VKShader()
|
||||
{
|
||||
VK_ALLOCATION_CALLBACKS
|
||||
const VKDevice &device = VKBackend::get().device_get();
|
||||
VKDevice &device = VKBackend::get().device_get();
|
||||
if (vertex_module_ != VK_NULL_HANDLE) {
|
||||
vkDestroyShaderModule(device.device_get(), vertex_module_, vk_allocation_callbacks);
|
||||
device.shader_modules.destruct(vertex_module_);
|
||||
vertex_module_ = VK_NULL_HANDLE;
|
||||
}
|
||||
if (geometry_module_ != VK_NULL_HANDLE) {
|
||||
vkDestroyShaderModule(device.device_get(), geometry_module_, vk_allocation_callbacks);
|
||||
device.shader_modules.destruct(geometry_module_);
|
||||
geometry_module_ = VK_NULL_HANDLE;
|
||||
}
|
||||
if (fragment_module_ != VK_NULL_HANDLE) {
|
||||
vkDestroyShaderModule(device.device_get(), fragment_module_, vk_allocation_callbacks);
|
||||
device.shader_modules.destruct(fragment_module_);
|
||||
fragment_module_ = VK_NULL_HANDLE;
|
||||
}
|
||||
if (compute_module_ != VK_NULL_HANDLE) {
|
||||
vkDestroyShaderModule(device.device_get(), compute_module_, vk_allocation_callbacks);
|
||||
device.shader_modules.destruct(compute_module_);
|
||||
compute_module_ = VK_NULL_HANDLE;
|
||||
}
|
||||
if (vk_pipeline_layout_ != VK_NULL_HANDLE) {
|
||||
@ -594,22 +599,16 @@ VKShader::~VKShader()
|
||||
}
|
||||
/* Reset not owning handles. */
|
||||
vk_descriptor_set_layout_ = VK_NULL_HANDLE;
|
||||
create_info_ = nullptr;
|
||||
}
|
||||
|
||||
void VKShader::build_shader_module(MutableSpan<const char *> sources,
|
||||
shaderc_shader_kind stage,
|
||||
VkShaderModule *r_shader_module)
|
||||
{
|
||||
BLI_assert_msg(ELEM(stage,
|
||||
shaderc_vertex_shader,
|
||||
shaderc_geometry_shader,
|
||||
shaderc_fragment_shader,
|
||||
shaderc_compute_shader),
|
||||
"Only forced ShaderC shader kinds are supported.");
|
||||
const VKDevice &device = VKBackend::get().device_get();
|
||||
VKDevice &device = VKBackend::get().device_get();
|
||||
sources[SOURCES_INDEX_VERSION] = device.glsl_patch_get();
|
||||
Vector<uint32_t> spirv_module = compile_glsl_to_spirv(sources, stage);
|
||||
build_shader_module(spirv_module, r_shader_module);
|
||||
device.shader_modules.construct(*this, *create_info_, sources, stage, r_shader_module);
|
||||
}
|
||||
|
||||
void VKShader::vertex_shader_from_glsl(MutableSpan<const char *> sources)
|
||||
|
@ -18,8 +18,11 @@
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKShaderInterface;
|
||||
class VKShaderModules;
|
||||
|
||||
class VKShader : public Shader {
|
||||
friend class VKShaderModules;
|
||||
|
||||
private:
|
||||
VKContext *context_ = nullptr;
|
||||
VkShaderModule vertex_module_ = VK_NULL_HANDLE;
|
||||
@ -43,13 +46,16 @@ class VKShader : public Shader {
|
||||
*/
|
||||
VkPipeline vk_pipeline_ = VK_NULL_HANDLE;
|
||||
|
||||
/** Create info used to construct this shader. */
|
||||
const shader::ShaderCreateInfo *create_info_ = nullptr;
|
||||
|
||||
public:
|
||||
VKPushConstants push_constants;
|
||||
|
||||
VKShader(const char *name);
|
||||
virtual ~VKShader();
|
||||
|
||||
void init(const shader::ShaderCreateInfo & /*info*/) override {}
|
||||
void init(const shader::ShaderCreateInfo &info) override;
|
||||
|
||||
void vertex_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
void geometry_shader_from_glsl(MutableSpan<const char *> sources) override;
|
||||
|
174
source/blender/gpu/vulkan/vk_shader_modules.cc
Normal file
174
source/blender/gpu/vulkan/vk_shader_modules.cc
Normal file
@ -0,0 +1,174 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#include "BLI_string_utils.hh"
|
||||
#include "BLI_time.h"
|
||||
|
||||
#include "gpu_shader_create_info.hh"
|
||||
|
||||
#include "vk_backend.hh"
|
||||
#include "vk_memory.hh"
|
||||
#include "vk_shader.hh"
|
||||
#include "vk_shader_log.hh"
|
||||
#include "vk_shader_modules.hh"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace blender::gpu {
|
||||
bool VKShaderModules::construct(VKShader &shader,
|
||||
const shader::ShaderCreateInfo & /*info*/,
|
||||
Span<const char *> sources,
|
||||
shaderc_shader_kind stage,
|
||||
VkShaderModule *r_shader_module)
|
||||
{
|
||||
BLI_assert_msg(ELEM(stage,
|
||||
shaderc_vertex_shader,
|
||||
shaderc_geometry_shader,
|
||||
shaderc_fragment_shader,
|
||||
shaderc_compute_shader),
|
||||
"Only forced ShaderC shader kinds are supported.");
|
||||
Vector<uint32_t> spirv_module;
|
||||
if (!compile_glsl_to_spirv(shader, sources, stage, spirv_module)) {
|
||||
r_shader_module = VK_NULL_HANDLE;
|
||||
return false;
|
||||
}
|
||||
return build_shader_module(shader, spirv_module, r_shader_module);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Frontend compilation (GLSL -> SpirV)
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static const std::string to_stage_name(shaderc_shader_kind stage)
|
||||
{
|
||||
switch (stage) {
|
||||
case shaderc_vertex_shader:
|
||||
return std::string("vertex");
|
||||
case shaderc_geometry_shader:
|
||||
return std::string("geometry");
|
||||
case shaderc_fragment_shader:
|
||||
return std::string("fragment");
|
||||
case shaderc_compute_shader:
|
||||
return std::string("compute");
|
||||
|
||||
default:
|
||||
BLI_assert_msg(false, "Do not know how to convert shaderc_shader_kind to stage name.");
|
||||
break;
|
||||
}
|
||||
return std::string("unknown stage");
|
||||
}
|
||||
|
||||
static std::string combine_sources(Span<const char *> sources)
|
||||
{
|
||||
char *sources_combined = BLI_string_join_arrayN((const char **)sources.data(), sources.size());
|
||||
std::string result(sources_combined);
|
||||
MEM_freeN(sources_combined);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool VKShaderModules::compile_glsl_to_spirv(VKShader &shader,
|
||||
Span<const char *> sources,
|
||||
shaderc_shader_kind stage,
|
||||
Vector<uint32_t> &r_compiled_spirv)
|
||||
{
|
||||
const double start_time = BLI_time_now_seconds();
|
||||
std::string combined_sources = combine_sources(sources);
|
||||
VKBackend &backend = VKBackend::get();
|
||||
shaderc::Compiler &compiler = backend.get_shaderc_compiler();
|
||||
shaderc::CompileOptions options;
|
||||
options.SetOptimizationLevel(shaderc_optimization_level_performance);
|
||||
options.SetTargetEnvironment(shaderc_target_env_vulkan, shaderc_env_version_vulkan_1_2);
|
||||
if (G.debug & G_DEBUG_GPU_RENDERDOC) {
|
||||
options.SetOptimizationLevel(shaderc_optimization_level_zero);
|
||||
options.SetGenerateDebugInfo();
|
||||
}
|
||||
|
||||
shaderc::SpvCompilationResult module = compiler.CompileGlslToSpv(
|
||||
combined_sources, stage, shader.name, options);
|
||||
if (module.GetNumErrors() != 0 || module.GetNumWarnings() != 0) {
|
||||
std::string log = module.GetErrorMessage();
|
||||
Vector<char> logcstr(log.c_str(), log.c_str() + log.size() + 1);
|
||||
|
||||
VKLogParser parser;
|
||||
shader.print_log(sources,
|
||||
logcstr.data(),
|
||||
to_stage_name(stage).c_str(),
|
||||
module.GetCompilationStatus() != shaderc_compilation_status_success,
|
||||
&parser);
|
||||
}
|
||||
stats_.glsl_to_spirv_time += BLI_time_now_seconds() - start_time;
|
||||
r_compiled_spirv.clear();
|
||||
if (module.GetCompilationStatus() != shaderc_compilation_status_success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
r_compiled_spirv.extend(module.cbegin(), module.cend());
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Frontend compilation (SpirV -> ShaderModule)
|
||||
*
|
||||
* \{ */
|
||||
bool VKShaderModules::build_shader_module(const VKShader &shader,
|
||||
Span<uint32_t> spirv_module,
|
||||
VkShaderModule *r_shader_module)
|
||||
{
|
||||
const double start_time = BLI_time_now_seconds();
|
||||
VK_ALLOCATION_CALLBACKS;
|
||||
|
||||
VkShaderModuleCreateInfo create_info = {};
|
||||
create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
create_info.codeSize = spirv_module.size() * sizeof(uint32_t);
|
||||
create_info.pCode = spirv_module.data();
|
||||
|
||||
const VKDevice &device = VKBackend::get().device_get();
|
||||
VkResult result = vkCreateShaderModule(
|
||||
device.device_get(), &create_info, vk_allocation_callbacks, r_shader_module);
|
||||
stats_.spirv_to_shader_module_time += BLI_time_now_seconds() - start_time;
|
||||
if (result == VK_SUCCESS) {
|
||||
debug::object_label(*r_shader_module, shader.name);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
*r_shader_module = VK_NULL_HANDLE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Debugging and statistics
|
||||
*
|
||||
* \{ */
|
||||
void VKShaderModules::debug_print() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "VKShaderModules(glsl_spirv_time=" << stats_.glsl_to_spirv_time << "s"
|
||||
<< ", spirv_shader_module_time" << stats_.spirv_to_shader_module_time << "s)\n";
|
||||
std::cout << ss.str();
|
||||
}
|
||||
/** \} */
|
||||
|
||||
void VKShaderModules::destruct(VkShaderModule vk_shader_module)
|
||||
{
|
||||
VK_ALLOCATION_CALLBACKS
|
||||
const VKDevice &device = VKBackend::get().device_get();
|
||||
vkDestroyShaderModule(device.device_get(), vk_shader_module, vk_allocation_callbacks);
|
||||
}
|
||||
|
||||
void VKShaderModules::free_data()
|
||||
{
|
||||
debug_print();
|
||||
}
|
||||
|
||||
} // namespace blender::gpu
|
59
source/blender/gpu/vulkan/vk_shader_modules.hh
Normal file
59
source/blender/gpu/vulkan/vk_shader_modules.hh
Normal file
@ -0,0 +1,59 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup gpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
#include "vk_common.hh"
|
||||
|
||||
#include "shaderc/shaderc.hpp"
|
||||
|
||||
namespace blender::gpu {
|
||||
class VKShader;
|
||||
namespace shader {
|
||||
class ShaderCreateInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pool of shader modules.
|
||||
*
|
||||
* Responsibility of VKShaderModules:
|
||||
* - Reusing of VkShaderModule between shaders to reduce compilation and pipeline creation
|
||||
* times.
|
||||
* - Loading of precompiled spirv bytecode.
|
||||
*/
|
||||
class VKShaderModules {
|
||||
private:
|
||||
struct {
|
||||
double glsl_to_spirv_time = 0.0;
|
||||
double spirv_to_shader_module_time = 0.0;
|
||||
} stats_;
|
||||
|
||||
public:
|
||||
bool construct(VKShader &shader,
|
||||
const shader::ShaderCreateInfo &info,
|
||||
Span<const char *> sources,
|
||||
shaderc_shader_kind stage,
|
||||
VkShaderModule *r_shader_module);
|
||||
void destruct(VkShaderModule vk_shader_module);
|
||||
void free_data();
|
||||
|
||||
void debug_print() const;
|
||||
|
||||
private:
|
||||
bool compile_glsl_to_spirv(VKShader &shader,
|
||||
Span<const char *> sources,
|
||||
shaderc_shader_kind stage,
|
||||
Vector<uint32_t> &r_compiled_spirv);
|
||||
bool build_shader_module(const VKShader &shader,
|
||||
Span<uint32_t> spirv_module,
|
||||
VkShaderModule *r_shader_module);
|
||||
};
|
||||
|
||||
} // namespace blender::gpu
|
Loading…
Reference in New Issue
Block a user