Compare commits
1 Commits
vulkan-2
...
vertex_spi
Author | SHA1 | Date | |
---|---|---|---|
5cdc08f6ea |
@ -221,7 +221,7 @@ public:
|
|||||||
|
|
||||||
int scope = 0;
|
int scope = 0;
|
||||||
|
|
||||||
private:
|
public:
|
||||||
void AddExpression(std::string_view text) {
|
void AddExpression(std::string_view text) {
|
||||||
if (!text.empty()) {
|
if (!text.empty()) {
|
||||||
shader_source.append(static_cast<std::size_t>(scope) * 4, ' ');
|
shader_source.append(static_cast<std::size_t>(scope) * 4, ' ');
|
||||||
@ -816,6 +816,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Generate() {
|
void Generate() {
|
||||||
|
bool dump = false;
|
||||||
if (sanitize_mul) {
|
if (sanitize_mul) {
|
||||||
#ifdef ANDROID
|
#ifdef ANDROID
|
||||||
// Use a cheaper sanitize_mul on Android, as mobile GPUs struggle here
|
// Use a cheaper sanitize_mul on Android, as mobile GPUs struggle here
|
||||||
@ -884,6 +885,8 @@ private:
|
|||||||
u32 compile_end = CompileRange(label, next_label);
|
u32 compile_end = CompileRange(label, next_label);
|
||||||
if (compile_end > next_label && compile_end != PROGRAM_END) {
|
if (compile_end > next_label && compile_end != PROGRAM_END) {
|
||||||
// This happens only when there is a label inside a IF/LOOP block
|
// This happens only when there is a label inside a IF/LOOP block
|
||||||
|
dump = true;
|
||||||
|
LOG_INFO(Render_OpenGL, "compile_end: {}", compile_end);
|
||||||
shader.AddLine("{{ jmp_to = {}u; break; }}", compile_end);
|
shader.AddLine("{{ jmp_to = {}u; break; }}", compile_end);
|
||||||
labels.emplace(compile_end);
|
labels.emplace(compile_end);
|
||||||
}
|
}
|
||||||
@ -906,6 +909,10 @@ private:
|
|||||||
|
|
||||||
DEBUG_ASSERT(shader.scope == 0);
|
DEBUG_ASSERT(shader.scope == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dump) {
|
||||||
|
LOG_INFO(Render_OpenGL, "{}", shader.shader_source);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -239,9 +239,17 @@ bool PipelineCache::UseProgrammableVertexShader(const Pica::Regs& regs,
|
|||||||
config.state.emulated_attrib_locations[location] = is_supported ? 0 : emulated_attrib_loc++;
|
config.state.emulated_attrib_locations[location] = is_supported ? 0 : emulated_attrib_loc++;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto [handle, result] =
|
vk::ShaderModule handle{};
|
||||||
programmable_vertex_shaders.Get(config, setup, vk::ShaderStageFlagBits::eVertex,
|
if (Settings::values.spirv_shader_gen.GetValue()) {
|
||||||
|
std::optional<std::vector<u32>> code;
|
||||||
|
std::tie(handle, code) = programmable_vertex_shaders_spv.Get(config, setup,
|
||||||
|
instance.GetDevice());
|
||||||
|
} else {
|
||||||
|
std::optional<std::string> code;
|
||||||
|
std::tie(handle, code) = programmable_vertex_shaders.Get(config, setup, vk::ShaderStageFlagBits::eVertex,
|
||||||
instance.GetDevice(), ShaderOptimization::High);
|
instance.GetDevice(), ShaderOptimization::High);
|
||||||
|
}
|
||||||
|
|
||||||
if (!handle) {
|
if (!handle) {
|
||||||
LOG_ERROR(Render_Vulkan, "Failed to retrieve programmable vertex shader");
|
LOG_ERROR(Render_Vulkan, "Failed to retrieve programmable vertex shader");
|
||||||
return false;
|
return false;
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "common/hash.h"
|
#include "common/hash.h"
|
||||||
#include "video_core/rasterizer_cache/pixel_format.h"
|
#include "video_core/rasterizer_cache/pixel_format.h"
|
||||||
#include "video_core/regs.h"
|
#include "video_core/regs.h"
|
||||||
|
#include "video_core/renderer_vulkan/vk_shader_decompiler.h"
|
||||||
#include "video_core/renderer_vulkan/vk_shader_gen_spv.h"
|
#include "video_core/renderer_vulkan/vk_shader_gen_spv.h"
|
||||||
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
#include "video_core/renderer_vulkan/vk_shader_util.h"
|
||||||
#include "video_core/shader/shader_cache.h"
|
#include "video_core/shader/shader_cache.h"
|
||||||
@ -111,7 +112,12 @@ struct PipelineInfo {
|
|||||||
* Vulkan specialized PICA shader caches
|
* Vulkan specialized PICA shader caches
|
||||||
*/
|
*/
|
||||||
using ProgrammableVertexShaders = Pica::Shader::ShaderDoubleCache<PicaVSConfig, vk::ShaderModule,
|
using ProgrammableVertexShaders = Pica::Shader::ShaderDoubleCache<PicaVSConfig, vk::ShaderModule,
|
||||||
&Compile, &GenerateVertexShader>;
|
std::string, &Compile,
|
||||||
|
&GenerateVertexShader>;
|
||||||
|
|
||||||
|
using ProgrammableVertexShadersSPV = Pica::Shader::ShaderDoubleCache<PicaVSConfig, vk::ShaderModule,
|
||||||
|
std::vector<u32>, &CompileSPV,
|
||||||
|
&GenerateVertexShaderSPV>;
|
||||||
|
|
||||||
using FixedGeometryShaders = Pica::Shader::ShaderCache<PicaFixedGSConfig, vk::ShaderModule,
|
using FixedGeometryShaders = Pica::Shader::ShaderCache<PicaFixedGSConfig, vk::ShaderModule,
|
||||||
&Compile, &GenerateFixedGeometryShader>;
|
&Compile, &GenerateFixedGeometryShader>;
|
||||||
@ -219,6 +225,7 @@ private:
|
|||||||
std::array<vk::ShaderModule, MAX_SHADER_STAGES> current_shaders;
|
std::array<vk::ShaderModule, MAX_SHADER_STAGES> current_shaders;
|
||||||
std::array<u64, MAX_SHADER_STAGES> shader_hashes;
|
std::array<u64, MAX_SHADER_STAGES> shader_hashes;
|
||||||
ProgrammableVertexShaders programmable_vertex_shaders;
|
ProgrammableVertexShaders programmable_vertex_shaders;
|
||||||
|
ProgrammableVertexShadersSPV programmable_vertex_shaders_spv;
|
||||||
FixedGeometryShaders fixed_geometry_shaders;
|
FixedGeometryShaders fixed_geometry_shaders;
|
||||||
FragmentShadersGLSL fragment_shaders_glsl;
|
FragmentShadersGLSL fragment_shaders_glsl;
|
||||||
FragmentShadersSPV fragment_shaders_spv;
|
FragmentShadersSPV fragment_shaders_spv;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,351 @@
|
|||||||
#ifndef VK_SHADER_DECOMPILER_H
|
// Copyright 2022 Citra Emulator Project
|
||||||
#define VK_SHADER_DECOMPILER_H
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
class vk_shader_decompiler
|
#include <exception>
|
||||||
{
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <optional>
|
||||||
|
#include <sirit/sirit.h>
|
||||||
|
#include <nihstro/shader_bytecode.h>
|
||||||
|
#include "video_core/renderer_vulkan/vk_shader_gen.h"
|
||||||
|
|
||||||
|
namespace Vulkan {
|
||||||
|
|
||||||
|
using Sirit::Id;
|
||||||
|
|
||||||
|
constexpr u32 PROGRAM_END = Pica::Shader::MAX_PROGRAM_CODE_LENGTH;
|
||||||
|
|
||||||
|
class DecompileFail : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
vk_shader_decompiler();
|
using std::runtime_error::runtime_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VK_SHADER_DECOMPILER_H
|
/// Describes the behaviour of code path of a given entry point and a return point.
|
||||||
|
enum class ExitMethod {
|
||||||
|
Undetermined, ///< Internal value. Only occur when analyzing JMP loop.
|
||||||
|
AlwaysReturn, ///< All code paths reach the return point.
|
||||||
|
Conditional, ///< Code path reaches the return point or an END instruction conditionally.
|
||||||
|
AlwaysEnd, ///< All code paths reach a END instruction.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A label is an offset into the code assigned to the SPIR-V lavel
|
||||||
|
struct Label {
|
||||||
|
u32 label;
|
||||||
|
mutable Id spv_label;
|
||||||
|
|
||||||
|
Label operator+(u32 other) const {
|
||||||
|
return Label{.label = label + other, .spv_label = spv_label};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const Label& other) const {
|
||||||
|
return label < other.label;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SpirvParams {
|
||||||
|
Id jmp_to; ///< Temporary holding the current jump target
|
||||||
|
Id while_label; ///< Label to the beginning of the while loop
|
||||||
|
Id switch_label; ///< Label to the beginning of the switch statement
|
||||||
|
Id switch_merge_block; ///< Label to the merge block of the switch statement
|
||||||
|
std::array<Id, 3> vars; ///< Available function variables used for LOOP
|
||||||
|
u32 used_vars = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A subroutine is a range of code refereced by a CALL, IF or LOOP instruction.
|
||||||
|
struct Subroutine {
|
||||||
|
u32 begin; ///< Entry point of the subroutine.
|
||||||
|
u32 end; ///< Return point of the subroutine.
|
||||||
|
ExitMethod exit_method; ///< Exit method of the subroutine.
|
||||||
|
std::set<u32> labels; ///< Addresses refereced by JMP instructions.
|
||||||
|
mutable Id function; ///< Function label of the subroutine
|
||||||
|
|
||||||
|
bool operator<(const Subroutine& rhs) const {
|
||||||
|
return std::tie(begin, end) < std::tie(rhs.begin, rhs.end);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Analyzes shader code and produces a set of subroutines.
|
||||||
|
class ControlFlowAnalyzer {
|
||||||
|
public:
|
||||||
|
ControlFlowAnalyzer(const Pica::Shader::ProgramCode& program_code, u32 main_offset);
|
||||||
|
|
||||||
|
[[nodiscard]] std::set<Subroutine> MoveSubroutines() {
|
||||||
|
return std::move(subroutines);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Adds and analyzes a new subroutine if it is not added yet.
|
||||||
|
const Subroutine& AddSubroutine(u32 begin, u32 end);
|
||||||
|
|
||||||
|
/// Merges exit method of two parallel branches.
|
||||||
|
ExitMethod ParallelExit(ExitMethod a, ExitMethod b);
|
||||||
|
|
||||||
|
/// Cascades exit method of two blocks of code.
|
||||||
|
ExitMethod SeriesExit(ExitMethod a, ExitMethod b);
|
||||||
|
|
||||||
|
/// Scans a range of code for labels and determines the exit method.
|
||||||
|
ExitMethod Scan(u32 begin, u32 end, std::set<u32>& labels);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Pica::Shader::ProgramCode& program_code;
|
||||||
|
std::set<Subroutine> subroutines;
|
||||||
|
std::map<std::pair<u32, u32>, ExitMethod> exit_method_map;
|
||||||
|
};
|
||||||
|
|
||||||
|
class VertexModule : public Sirit::Module {
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
VertexModule(const Pica::Shader::ShaderSetup& setup,
|
||||||
|
const PicaVSConfig& config);
|
||||||
|
~VertexModule();
|
||||||
|
|
||||||
|
void Generate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Gets the Subroutine object corresponding to the specified address.
|
||||||
|
const Subroutine& GetSubroutine(u32 begin, u32 end) const;
|
||||||
|
|
||||||
|
/// Generates code to evaluate a shader control flow instruction
|
||||||
|
Id EvaluateCondition(nihstro::Instruction::FlowControlType flow_control);
|
||||||
|
|
||||||
|
/// Generates code representing a source register.
|
||||||
|
Id GetSourceRegister(const SourceRegister& source_reg, u32 address_register_index);
|
||||||
|
|
||||||
|
/// Generates code representing a destination register.
|
||||||
|
Id GetDestRegister(const DestRegister& dest_reg);
|
||||||
|
|
||||||
|
/// Returns the pointer type of the destination register.
|
||||||
|
Id GetDestPointer(const DestRegister& dest_reg);
|
||||||
|
|
||||||
|
/// Attemps to sanitize multiplication result to match PICA expected behaviour.
|
||||||
|
Id SanitizeMul(Id lhs, Id rhs);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds code that calls a subroutine.
|
||||||
|
* @param subroutine the subroutine to call.
|
||||||
|
*/
|
||||||
|
void CallSubroutine(const Subroutine& subroutine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes code that does an assignment operation.
|
||||||
|
* @param swizzle the swizzle data of the current instruction.
|
||||||
|
* @param reg the destination register code.
|
||||||
|
* @param value the code representing the value to assign.
|
||||||
|
* @param storage_class storage specifier of reg.
|
||||||
|
* @param value_num_components number of components of the value to assign.
|
||||||
|
*/
|
||||||
|
void SetDest(const nihstro::SwizzlePattern& swizzle, Id reg, Id value,
|
||||||
|
Id reg_pointer, u32 dest_num_components, u32 value_num_components);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles a single instruction from PICA to GLSL.
|
||||||
|
* @param offset the offset of the PICA shader instruction.
|
||||||
|
* @return the offset of the next instruction to execute. Usually it is the current offset + 1.
|
||||||
|
* If the current instruction is IF or LOOP, the next instruction is after the IF or LOOP block.
|
||||||
|
* If the current instruction always terminates the program, returns PROGRAM_END.
|
||||||
|
*/
|
||||||
|
u32 CompileInstr(u32 offset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compiles a range of instructions from PICA to GLSL.
|
||||||
|
* @param begin the offset of the starting instruction.
|
||||||
|
* @param end the offset where the compilation should stop (exclusive).
|
||||||
|
* @return the offset of the next instruction to compile. PROGRAM_END if the program terminates.
|
||||||
|
*/
|
||||||
|
u32 CompileRange(u32 begin, u32 end);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Returns an id of the attribute type
|
||||||
|
Id AttribType(u32 index) const {
|
||||||
|
switch (config.state.attrib_types[index]) {
|
||||||
|
case Pica::PipelineRegs::VertexAttributeFormat::FLOAT:
|
||||||
|
return vec_ids.Get(4);
|
||||||
|
case Pica::PipelineRegs::VertexAttributeFormat::BYTE:
|
||||||
|
case Pica::PipelineRegs::VertexAttributeFormat::SHORT:
|
||||||
|
return ivec_ids.Get(4);
|
||||||
|
case Pica::PipelineRegs::VertexAttributeFormat::UBYTE:
|
||||||
|
return uvec_ids.Get(4);
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
return Id{};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the attribute casted to float
|
||||||
|
Id AttribCast(u32 index, Id typed_reg) {
|
||||||
|
switch (config.state.attrib_types[index]) {
|
||||||
|
case Pica::PipelineRegs::VertexAttributeFormat::FLOAT:
|
||||||
|
break;
|
||||||
|
case Pica::PipelineRegs::VertexAttributeFormat::BYTE:
|
||||||
|
case Pica::PipelineRegs::VertexAttributeFormat::SHORT:
|
||||||
|
return OpConvertSToF(ivec_ids.Get(4), typed_reg);
|
||||||
|
case Pica::PipelineRegs::VertexAttributeFormat::UBYTE:
|
||||||
|
return OpConvertUToF(uvec_ids.Get(4), typed_reg);
|
||||||
|
default:
|
||||||
|
UNREACHABLE();
|
||||||
|
}
|
||||||
|
return typed_reg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads the member specified from the vs_uniforms uniform struct
|
||||||
|
template <typename... Ids>
|
||||||
|
[[nodiscard]] Id GetVsUniformMember(Id type, Ids... ids) {
|
||||||
|
const Id uniform_ptr{TypePointer(spv::StorageClass::Uniform, type)};
|
||||||
|
return OpLoad(type, OpAccessChain(uniform_ptr, vs_uniforms, ids...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates code representing a bool uniform
|
||||||
|
Id GetUniformBool(u32 index) {
|
||||||
|
const Id value{GetVsUniformMember(u32_id, ConstU32(0u), ConstU32(index))};
|
||||||
|
return OpINotEqual(bool_id, value, ConstU32(0u));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines a input variable
|
||||||
|
[[nodiscard]] Id DefineInput(Id type, u32 location) {
|
||||||
|
const Id input_id{DefineVar(type, spv::StorageClass::Input)};
|
||||||
|
Decorate(input_id, spv::Decoration::Location, location);
|
||||||
|
return input_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines a input variable
|
||||||
|
[[nodiscard]] Id DefineOutput(Id type, u32 location) {
|
||||||
|
const Id output_id{DefineVar(type, spv::StorageClass::Output)};
|
||||||
|
Decorate(output_id, spv::Decoration::Location, location);
|
||||||
|
return output_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Defines a uniform constant variable
|
||||||
|
[[nodiscard]] Id DefineUniformConst(Id type, u32 set, u32 binding, bool readonly = false) {
|
||||||
|
const Id uniform_id{DefineVar(type, spv::StorageClass::UniformConstant)};
|
||||||
|
Decorate(uniform_id, spv::Decoration::DescriptorSet, set);
|
||||||
|
Decorate(uniform_id, spv::Decoration::Binding, binding);
|
||||||
|
if (readonly) {
|
||||||
|
Decorate(uniform_id, spv::Decoration::NonWritable);
|
||||||
|
}
|
||||||
|
return uniform_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool global = true>
|
||||||
|
[[nodiscard]] Id DefineVar(Id type, spv::StorageClass storage_class) {
|
||||||
|
const Id pointer_type_id{TypePointer(storage_class, type)};
|
||||||
|
return global ? AddGlobalVariable(pointer_type_id, storage_class)
|
||||||
|
: AddLocalVariable(pointer_type_id, storage_class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the id of a signed integer constant of value
|
||||||
|
[[nodiscard]] Id ConstBool(bool value) {
|
||||||
|
return value ? ConstantTrue(bool_id) : ConstantFalse(bool_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
[[nodiscard]] Id ConstBool(Args&&... values) {
|
||||||
|
constexpr u32 size = static_cast<u32>(sizeof...(values));
|
||||||
|
static_assert(size >= 2);
|
||||||
|
const std::array constituents{ConstBool(values)...};
|
||||||
|
const Id type = size <= 4 ? bvec_ids.Get(size) : TypeArray(bool_id, ConstU32(size));
|
||||||
|
return ConstantComposite(type, constituents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the id of a signed integer constant of value
|
||||||
|
[[nodiscard]] Id ConstU32(u32 value) {
|
||||||
|
return Constant(u32_id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
[[nodiscard]] Id ConstU32(Args&&... values) {
|
||||||
|
constexpr u32 size = static_cast<u32>(sizeof...(values));
|
||||||
|
static_assert(size >= 2);
|
||||||
|
const std::array constituents{Constant(u32_id, values)...};
|
||||||
|
const Id type = size <= 4 ? uvec_ids.Get(size) : TypeArray(u32_id, ConstU32(size));
|
||||||
|
return ConstantComposite(type, constituents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the id of a signed integer constant of value
|
||||||
|
[[nodiscard]] Id ConstS32(s32 value) {
|
||||||
|
return Constant(i32_id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
[[nodiscard]] Id ConstS32(Args&&... values) {
|
||||||
|
constexpr u32 size = static_cast<u32>(sizeof...(values));
|
||||||
|
static_assert(size >= 2);
|
||||||
|
const std::array constituents{Constant(i32_id, values)...};
|
||||||
|
const Id type = size <= 4 ? ivec_ids.Get(size) : TypeArray(i32_id, ConstU32(size));
|
||||||
|
return ConstantComposite(type, constituents);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the id of a float constant of value
|
||||||
|
[[nodiscard]] Id ConstF32(f32 value) {
|
||||||
|
return Constant(f32_id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Args>
|
||||||
|
[[nodiscard]] Id ConstF32(Args... values) {
|
||||||
|
constexpr u32 size = static_cast<u32>(sizeof...(values));
|
||||||
|
static_assert(size >= 2);
|
||||||
|
const std::array constituents{Constant(f32_id, values)...};
|
||||||
|
const Id type = size <= 4 ? vec_ids.Get(size) : TypeArray(f32_id, ConstU32(size));
|
||||||
|
return ConstantComposite(type, constituents);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefineArithmeticTypes();
|
||||||
|
void DefineEntryPoint();
|
||||||
|
void DefineUniformStructs();
|
||||||
|
void DefineInterface();
|
||||||
|
|
||||||
|
public:
|
||||||
|
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{};
|
||||||
|
|
||||||
|
private:
|
||||||
|
const PicaVSConfig& config;
|
||||||
|
const Pica::Shader::ProgramCode& program_code;
|
||||||
|
const Pica::Shader::SwizzleData& swizzle_data;
|
||||||
|
u32 main_offset;
|
||||||
|
bool sanitize_mul;
|
||||||
|
std::set<Subroutine> subroutines;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PICA input registers are float but vulkan doesn't have the
|
||||||
|
* ability to cast integer attributes to float. Thus they are
|
||||||
|
* manually cast if needed
|
||||||
|
**/
|
||||||
|
std::array<Id, 16> input_typed_regs{};
|
||||||
|
std::array<Id, 16> input_regs{};
|
||||||
|
std::array<bool, 16> used_regs{};
|
||||||
|
std::array<Id, 16> output_regs{};
|
||||||
|
std::array<Id, 16> tmp_regs{};
|
||||||
|
|
||||||
|
Id vs_uniforms{};
|
||||||
|
Id conditional_code{};
|
||||||
|
Id address_registers{};
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates the SPIRV vertex shader program source code for the given VS program
|
||||||
|
* @returns String of the shader source code; boost::none on failure
|
||||||
|
*/
|
||||||
|
std::optional<std::vector<u32>> GenerateVertexShaderSPV(const Pica::Shader::ShaderSetup& setup,
|
||||||
|
const PicaVSConfig& config);
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace Vulkan
|
||||||
|
@ -1676,10 +1676,11 @@ std::optional<std::string> GenerateVertexShader(const Pica::Shader::ShaderSetup&
|
|||||||
std::string& program_source = program_source_opt->code;
|
std::string& program_source = program_source_opt->code;
|
||||||
|
|
||||||
out += R"(
|
out += R"(
|
||||||
#define uniforms vs_uniforms
|
|
||||||
layout (set = 0, binding = 0, std140) uniform vs_config {
|
layout (set = 0, binding = 0, std140) uniform vs_config {
|
||||||
pica_uniforms uniforms;
|
bool b[16];
|
||||||
};
|
uvec4 i[4];
|
||||||
|
vec4 f[96];
|
||||||
|
} uniforms;
|
||||||
|
|
||||||
)";
|
)";
|
||||||
if (!config.state.use_geometry_shader) {
|
if (!config.state.use_geometry_shader) {
|
||||||
@ -1824,7 +1825,6 @@ layout (set = 0, binding = 0, std140) uniform vs_config {
|
|||||||
|
|
||||||
out += program_source;
|
out += program_source;
|
||||||
|
|
||||||
LOG_INFO(Render_Vulkan, "{}", out);
|
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,8 @@
|
|||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/microprofile.h"
|
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "video_core/regs.h"
|
|
||||||
#include "video_core/renderer_vulkan/vk_shader_gen_spv.h"
|
#include "video_core/renderer_vulkan/vk_shader_gen_spv.h"
|
||||||
#include "video_core/shader/shader_uniforms.h"
|
|
||||||
|
|
||||||
using Pica::FramebufferRegs;
|
using Pica::FramebufferRegs;
|
||||||
using Pica::LightingRegs;
|
using Pica::LightingRegs;
|
||||||
|
@ -12,19 +12,19 @@ namespace Vulkan {
|
|||||||
|
|
||||||
using Sirit::Id;
|
using Sirit::Id;
|
||||||
|
|
||||||
struct VectorIds {
|
class FragmentModule : public Sirit::Module {
|
||||||
|
static constexpr u32 NUM_TEV_STAGES = 6;
|
||||||
|
static constexpr u32 NUM_LIGHTS = 8;
|
||||||
|
static constexpr u32 NUM_LIGHTING_SAMPLERS = 24;
|
||||||
|
|
||||||
|
struct VectorIds {
|
||||||
/// Returns the type id of the vector with the provided size
|
/// Returns the type id of the vector with the provided size
|
||||||
[[nodiscard]] constexpr Id Get(u32 size) const {
|
[[nodiscard]] constexpr Id Get(u32 size) const {
|
||||||
return ids[size - 2];
|
return ids[size - 2];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<Id, 3> ids;
|
std::array<Id, 3> ids;
|
||||||
};
|
};
|
||||||
|
|
||||||
class FragmentModule : public Sirit::Module {
|
|
||||||
static constexpr u32 NUM_TEV_STAGES = 6;
|
|
||||||
static constexpr u32 NUM_LIGHTS = 8;
|
|
||||||
static constexpr u32 NUM_LIGHTING_SAMPLERS = 24;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FragmentModule(const PicaFSConfig& config);
|
FragmentModule(const PicaFSConfig& config);
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
namespace Pica::Shader {
|
namespace Pica::Shader {
|
||||||
|
|
||||||
template <typename ShaderType>
|
template <typename ShaderType, typename ShaderBinary>
|
||||||
using ShaderCacheResult = std::pair<ShaderType, std::optional<std::string>>;
|
using ShaderCacheResult = std::pair<ShaderType, std::optional<ShaderBinary>>;
|
||||||
|
|
||||||
template <typename KeyType, typename ShaderType, auto ModuleCompiler, auto CodeGenerator>
|
template <typename KeyType, typename ShaderType, auto ModuleCompiler, auto CodeGenerator>
|
||||||
class ShaderCache {
|
class ShaderCache {
|
||||||
@ -50,7 +50,8 @@ public:
|
|||||||
* program buffer from the previous shader, which is hashed into the config, resulting several
|
* program buffer from the previous shader, which is hashed into the config, resulting several
|
||||||
* different config values from the same shader program.
|
* different config values from the same shader program.
|
||||||
*/
|
*/
|
||||||
template <typename KeyType, typename ShaderType, auto ModuleCompiler, auto CodeGenerator>
|
template <typename KeyType, typename ShaderType, typename ShaderBinary,
|
||||||
|
auto ModuleCompiler, auto CodeGenerator>
|
||||||
class ShaderDoubleCache {
|
class ShaderDoubleCache {
|
||||||
public:
|
public:
|
||||||
ShaderDoubleCache() = default;
|
ShaderDoubleCache() = default;
|
||||||
@ -58,7 +59,7 @@ public:
|
|||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
auto Get(const KeyType& key, const Pica::Shader::ShaderSetup& setup, Args&&... args)
|
auto Get(const KeyType& key, const Pica::Shader::ShaderSetup& setup, Args&&... args)
|
||||||
-> ShaderCacheResult<ShaderType> {
|
-> ShaderCacheResult<ShaderType, ShaderBinary> {
|
||||||
if (auto map_iter = shader_map.find(key); map_iter == shader_map.end()) {
|
if (auto map_iter = shader_map.find(key); map_iter == shader_map.end()) {
|
||||||
auto code = CodeGenerator(setup, key);
|
auto code = CodeGenerator(setup, key);
|
||||||
if (!code) {
|
if (!code) {
|
||||||
@ -66,7 +67,7 @@ public:
|
|||||||
return std::make_pair(ShaderType{}, std::nullopt);
|
return std::make_pair(ShaderType{}, std::nullopt);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string& program = code.value();
|
const ShaderBinary& program = code.value();
|
||||||
auto [iter, new_shader] = shader_cache.emplace(program, ShaderType{});
|
auto [iter, new_shader] = shader_cache.emplace(program, ShaderType{});
|
||||||
auto& shader = iter->second;
|
auto& shader = iter->second;
|
||||||
|
|
||||||
@ -81,7 +82,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Inject(const KeyType& key, std::string decomp, ShaderType&& program) {
|
void Inject(const KeyType& key, ShaderBinary&& decomp, ShaderType&& program) {
|
||||||
const auto iter = shader_cache.emplace(std::move(decomp), std::move(program)).first;
|
const auto iter = shader_cache.emplace(std::move(decomp), std::move(program)).first;
|
||||||
|
|
||||||
auto& cached_shader = iter->second;
|
auto& cached_shader = iter->second;
|
||||||
@ -90,7 +91,16 @@ public:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
std::unordered_map<KeyType, ShaderType*> shader_map;
|
std::unordered_map<KeyType, ShaderType*> shader_map;
|
||||||
std::unordered_map<std::string, ShaderType> shader_cache;
|
std::unordered_map<ShaderBinary, ShaderType> shader_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Pica::Shader
|
} // namespace Pica::Shader
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template <>
|
||||||
|
struct hash<std::vector<u32>> {
|
||||||
|
std::size_t operator()(const std::vector<u32>& code) const noexcept {
|
||||||
|
return Common::ComputeHash64(code.data(), code.size() * sizeof(u32));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace std
|
||||||
|
Reference in New Issue
Block a user