renderer_vulkan: Initialize SPIRV vertex shaders
Defines shaders as an array of `u32` rather than a GLSL string. Uses a hash of the `u32` bytes rather than `std::string`
This commit is contained in:
parent
d24722ba96
commit
882f75c02c
7 changed files with 175 additions and 20 deletions
|
@ -77,6 +77,10 @@ add_library(video_core STATIC
|
|||
shader/generator/shader_gen.h
|
||||
shader/generator/shader_uniforms.cpp
|
||||
shader/generator/shader_uniforms.h
|
||||
shader/generator/spv_fs_shader_gen.cpp
|
||||
shader/generator/spv_fs_shader_gen.h
|
||||
shader/generator/spv_shader_gen.h
|
||||
shader/generator/spv_shader_gen.cpp
|
||||
shader/shader.cpp
|
||||
shader/shader.h
|
||||
shader/shader_interpreter.cpp
|
||||
|
|
|
@ -160,7 +160,7 @@ struct Shader : public Common::AsyncHandle {
|
|||
|
||||
vk::ShaderModule module;
|
||||
vk::Device device;
|
||||
std::string program;
|
||||
std::vector<u32> program;
|
||||
};
|
||||
|
||||
class GraphicsPipeline : public Common::AsyncHandle {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "video_core/shader/generator/glsl_fs_shader_gen.h"
|
||||
#include "video_core/shader/generator/glsl_shader_gen.h"
|
||||
#include "video_core/shader/generator/spv_fs_shader_gen.h"
|
||||
#include "video_core/shader/generator/spv_shader_gen.h"
|
||||
|
||||
using namespace Pica::Shader::Generator;
|
||||
using Pica::Shader::FSConfig;
|
||||
|
@ -404,21 +405,37 @@ bool PipelineCache::UseProgrammableVertexShader(const Pica::RegsInternal& regs,
|
|||
|
||||
auto [it, new_config] = programmable_vertex_map.try_emplace(config);
|
||||
if (new_config) {
|
||||
auto program = GLSL::GenerateVertexShader(setup, config, true);
|
||||
const bool use_spirv = Settings::values.spirv_shader_gen.GetValue();
|
||||
const vk::Device device = instance.GetDevice();
|
||||
|
||||
std::vector<u32> code;
|
||||
|
||||
// Disabled for programmable shaders for now
|
||||
if (use_spirv && false) {
|
||||
// Directly generate SPIRV
|
||||
code = SPIRV::GenerateVertexShader(setup, config, profile);
|
||||
} else {
|
||||
// Generate GLSL
|
||||
const std::string program = GLSL::GenerateVertexShader(setup, config, true);
|
||||
if (program.empty()) {
|
||||
LOG_ERROR(Render_Vulkan, "Failed to retrieve programmable vertex shader");
|
||||
programmable_vertex_map[config] = nullptr;
|
||||
return false;
|
||||
}
|
||||
// Compile GLSL to SPIRV
|
||||
code = CompileGLSLtoSPIRV(program, vk::ShaderStageFlagBits::eVertex, device);
|
||||
}
|
||||
|
||||
auto [iter, new_program] = programmable_vertex_cache.try_emplace(program, instance);
|
||||
const u64 code_hash = Common::ComputeHash64(std::as_bytes(std::span(code)));
|
||||
|
||||
auto [iter, new_program] = programmable_vertex_cache.try_emplace(code_hash, instance);
|
||||
auto& shader = iter->second;
|
||||
|
||||
// Queue worker thread to create shader module
|
||||
if (new_program) {
|
||||
shader.program = std::move(program);
|
||||
const vk::Device device = instance.GetDevice();
|
||||
shader.program = std::move(code);
|
||||
workers.QueueWork([device, &shader] {
|
||||
shader.module = Compile(shader.program, vk::ShaderStageFlagBits::eVertex, device);
|
||||
shader.module = CompileSPV(shader.program, device);
|
||||
shader.MarkDone();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ private:
|
|||
std::array<u64, MAX_SHADER_STAGES> shader_hashes;
|
||||
std::array<Shader*, MAX_SHADER_STAGES> current_shaders;
|
||||
std::unordered_map<Pica::Shader::Generator::PicaVSConfig, Shader*> programmable_vertex_map;
|
||||
std::unordered_map<std::string, Shader> programmable_vertex_cache;
|
||||
std::unordered_map<u64, Shader> programmable_vertex_cache;
|
||||
std::unordered_map<Pica::Shader::Generator::PicaFixedGSConfig, Shader> fixed_geometry_shaders;
|
||||
std::unordered_map<Pica::Shader::FSConfig, Shader> fragment_shaders;
|
||||
Shader trivial_vertex_shader;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include <array>
|
||||
#include <sirit/sirit.h>
|
||||
|
||||
#include "spv_shader_gen.h"
|
||||
#include "video_core/pica/regs_framebuffer.h"
|
||||
#include "video_core/pica/regs_texturing.h"
|
||||
|
||||
|
@ -19,15 +20,6 @@ namespace Pica::Shader::Generator::SPIRV {
|
|||
|
||||
using Sirit::Id;
|
||||
|
||||
struct VectorIds {
|
||||
/// Returns the type id of the vector with the provided size
|
||||
[[nodiscard]] constexpr Id Get(u32 size) const {
|
||||
return ids[size - 2];
|
||||
}
|
||||
|
||||
std::array<Id, 3> ids;
|
||||
};
|
||||
|
||||
class FragmentModule : public Sirit::Module {
|
||||
static constexpr u32 NUM_TEV_STAGES = 6;
|
||||
static constexpr u32 NUM_LIGHTS = 8;
|
||||
|
|
68
src/video_core/shader/generator/spv_shader_gen.cpp
Normal file
68
src/video_core/shader/generator/spv_shader_gen.cpp
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2024 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "video_core/pica/regs_rasterizer.h"
|
||||
#include "video_core/shader/generator/shader_gen.h"
|
||||
// #include "video_core/shader/generator/spv_shader_decompiler.h"
|
||||
#include "video_core/shader/generator/spv_shader_gen.h"
|
||||
|
||||
using VSOutputAttributes = Pica::RasterizerRegs::VSOutputAttributes;
|
||||
|
||||
namespace Pica::Shader::Generator::SPIRV {
|
||||
|
||||
constexpr u32 SPIRV_VERSION_1_3 = 0x00010300;
|
||||
|
||||
VertexModule::VertexModule(const PicaVSConfig& config_, const Profile& profile_)
|
||||
: Sirit::Module{SPIRV_VERSION_1_3}, config{config_}, profile{profile_} {
|
||||
DefineArithmeticTypes();
|
||||
DefineInterface();
|
||||
DefineEntryPoint();
|
||||
}
|
||||
|
||||
VertexModule::~VertexModule() = default;
|
||||
|
||||
void VertexModule::Generate() {
|
||||
AddLabel(OpLabel());
|
||||
OpReturn();
|
||||
OpFunctionEnd();
|
||||
}
|
||||
|
||||
void VertexModule::DefineArithmeticTypes() {
|
||||
void_id = Name(TypeVoid(), "void_id");
|
||||
bool_id = Name(TypeBool(), "bool_id");
|
||||
f32_id = Name(TypeFloat(32), "f32_id");
|
||||
i32_id = Name(TypeSInt(32), "i32_id");
|
||||
u32_id = Name(TypeUInt(32), "u32_id");
|
||||
|
||||
for (u32 size = 2; size <= 4; size++) {
|
||||
const u32 i = size - 2;
|
||||
vec_ids.ids[i] = Name(TypeVector(f32_id, size), fmt::format("vec{}_id", size));
|
||||
ivec_ids.ids[i] = Name(TypeVector(i32_id, size), fmt::format("ivec{}_id", size));
|
||||
uvec_ids.ids[i] = Name(TypeVector(u32_id, size), fmt::format("uvec{}_id", size));
|
||||
bvec_ids.ids[i] = Name(TypeVector(bool_id, size), fmt::format("bvec{}_id", size));
|
||||
}
|
||||
}
|
||||
|
||||
void VertexModule::DefineEntryPoint() {
|
||||
AddCapability(spv::Capability::Shader);
|
||||
SetMemoryModel(spv::AddressingModel::Logical, spv::MemoryModel::GLSL450);
|
||||
|
||||
const Id main_type{TypeFunction(TypeVoid())};
|
||||
const Id main_func{OpFunction(TypeVoid(), spv::FunctionControlMask::MaskNone, main_type)};
|
||||
|
||||
AddEntryPoint(spv::ExecutionModel::Vertex, main_func, "main");
|
||||
}
|
||||
|
||||
void VertexModule::DefineInterface() {
|
||||
// Define interface block
|
||||
}
|
||||
|
||||
std::vector<u32> GenerateVertexShader(const ShaderSetup& setup, const PicaVSConfig& config,
|
||||
const Profile& profile) {
|
||||
VertexModule module(config, profile);
|
||||
module.Generate();
|
||||
return module.Assemble();
|
||||
}
|
||||
|
||||
} // namespace Pica::Shader::Generator::SPIRV
|
74
src/video_core/shader/generator/spv_shader_gen.h
Normal file
74
src/video_core/shader/generator/spv_shader_gen.h
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2024 Citra Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sirit/sirit.h>
|
||||
|
||||
namespace Pica {
|
||||
struct ShaderSetup;
|
||||
}
|
||||
|
||||
namespace Pica::Shader {
|
||||
struct VSConfig;
|
||||
struct Profile;
|
||||
} // namespace Pica::Shader
|
||||
|
||||
namespace Pica::Shader::Generator {
|
||||
struct PicaVSConfig;
|
||||
} // namespace Pica::Shader::Generator
|
||||
|
||||
namespace Pica::Shader::Generator::SPIRV {
|
||||
|
||||
using Sirit::Id;
|
||||
|
||||
struct VectorIds {
|
||||
/// Returns the type id of the vector with the provided size
|
||||
[[nodiscard]] constexpr Id Get(u32 size) const {
|
||||
return ids[size - 2];
|
||||
}
|
||||
|
||||
std::array<Id, 3> ids;
|
||||
};
|
||||
|
||||
class VertexModule : public Sirit::Module {
|
||||
|
||||
public:
|
||||
explicit VertexModule(const PicaVSConfig& config, const Profile& profile);
|
||||
~VertexModule();
|
||||
|
||||
/// Emits SPIR-V bytecode corresponding to the provided pica vertex configuration
|
||||
void Generate();
|
||||
|
||||
private:
|
||||
void DefineArithmeticTypes();
|
||||
void DefineEntryPoint();
|
||||
void DefineInterface();
|
||||
|
||||
private:
|
||||
const PicaVSConfig& config;
|
||||
const Profile& profile;
|
||||
|
||||
Id void_id{};
|
||||
Id bool_id{};
|
||||
Id f32_id{};
|
||||
Id i32_id{};
|
||||
Id u32_id{};
|
||||
|
||||
VectorIds vec_ids{};
|
||||
VectorIds ivec_ids{};
|
||||
VectorIds uvec_ids{};
|
||||
VectorIds bvec_ids{};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates the SPIRV vertex shader program source code for the given VS program
|
||||
* @param config ShaderCacheKey object generated for the current Pica state, used for the shader
|
||||
* configuration (NOTE: Use state in this struct only, not the Pica registers!)
|
||||
* @param separable_shader generates shader that can be used for separate shader object
|
||||
* @returns String of the shader source code; empty on failure
|
||||
*/
|
||||
std::vector<u32> GenerateVertexShader(const Pica::ShaderSetup& setup, const PicaVSConfig& config,
|
||||
const Profile& profile);
|
||||
} // namespace Pica::Shader::Generator::SPIRV
|
Loading…
Reference in a new issue