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_gen.h
|
||||||
shader/generator/shader_uniforms.cpp
|
shader/generator/shader_uniforms.cpp
|
||||||
shader/generator/shader_uniforms.h
|
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.cpp
|
||||||
shader/shader.h
|
shader/shader.h
|
||||||
shader/shader_interpreter.cpp
|
shader/shader_interpreter.cpp
|
||||||
|
|
|
@ -160,7 +160,7 @@ struct Shader : public Common::AsyncHandle {
|
||||||
|
|
||||||
vk::ShaderModule module;
|
vk::ShaderModule module;
|
||||||
vk::Device device;
|
vk::Device device;
|
||||||
std::string program;
|
std::vector<u32> program;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GraphicsPipeline : public Common::AsyncHandle {
|
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_fs_shader_gen.h"
|
||||||
#include "video_core/shader/generator/glsl_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_fs_shader_gen.h"
|
||||||
|
#include "video_core/shader/generator/spv_shader_gen.h"
|
||||||
|
|
||||||
using namespace Pica::Shader::Generator;
|
using namespace Pica::Shader::Generator;
|
||||||
using Pica::Shader::FSConfig;
|
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);
|
auto [it, new_config] = programmable_vertex_map.try_emplace(config);
|
||||||
if (new_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()) {
|
if (program.empty()) {
|
||||||
LOG_ERROR(Render_Vulkan, "Failed to retrieve programmable vertex shader");
|
LOG_ERROR(Render_Vulkan, "Failed to retrieve programmable vertex shader");
|
||||||
programmable_vertex_map[config] = nullptr;
|
programmable_vertex_map[config] = nullptr;
|
||||||
return false;
|
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;
|
auto& shader = iter->second;
|
||||||
|
|
||||||
|
// Queue worker thread to create shader module
|
||||||
if (new_program) {
|
if (new_program) {
|
||||||
shader.program = std::move(program);
|
shader.program = std::move(code);
|
||||||
const vk::Device device = instance.GetDevice();
|
|
||||||
workers.QueueWork([device, &shader] {
|
workers.QueueWork([device, &shader] {
|
||||||
shader.module = Compile(shader.program, vk::ShaderStageFlagBits::eVertex, device);
|
shader.module = CompileSPV(shader.program, device);
|
||||||
shader.MarkDone();
|
shader.MarkDone();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ private:
|
||||||
std::array<u64, MAX_SHADER_STAGES> shader_hashes;
|
std::array<u64, MAX_SHADER_STAGES> shader_hashes;
|
||||||
std::array<Shader*, MAX_SHADER_STAGES> current_shaders;
|
std::array<Shader*, MAX_SHADER_STAGES> current_shaders;
|
||||||
std::unordered_map<Pica::Shader::Generator::PicaVSConfig, Shader*> programmable_vertex_map;
|
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::Generator::PicaFixedGSConfig, Shader> fixed_geometry_shaders;
|
||||||
std::unordered_map<Pica::Shader::FSConfig, Shader> fragment_shaders;
|
std::unordered_map<Pica::Shader::FSConfig, Shader> fragment_shaders;
|
||||||
Shader trivial_vertex_shader;
|
Shader trivial_vertex_shader;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <sirit/sirit.h>
|
#include <sirit/sirit.h>
|
||||||
|
|
||||||
|
#include "spv_shader_gen.h"
|
||||||
#include "video_core/pica/regs_framebuffer.h"
|
#include "video_core/pica/regs_framebuffer.h"
|
||||||
#include "video_core/pica/regs_texturing.h"
|
#include "video_core/pica/regs_texturing.h"
|
||||||
|
|
||||||
|
@ -19,15 +20,6 @@ namespace Pica::Shader::Generator::SPIRV {
|
||||||
|
|
||||||
using Sirit::Id;
|
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 {
|
class FragmentModule : public Sirit::Module {
|
||||||
static constexpr u32 NUM_TEV_STAGES = 6;
|
static constexpr u32 NUM_TEV_STAGES = 6;
|
||||||
static constexpr u32 NUM_LIGHTS = 8;
|
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