[SPIR-V] Version, float controls

This commit is contained in:
Triang3l 2020-10-31 16:22:15 +03:00
parent cacf702948
commit 52a8ed8e6d
7 changed files with 122 additions and 34 deletions

View File

@ -108,9 +108,10 @@ int shader_compiler_main(const std::vector<std::string>& args) {
shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size());
std::unique_ptr<ShaderTranslator> translator;
SpirvShaderTranslator::Features spirv_features(true);
if (cvars::shader_output_type == "spirv" ||
cvars::shader_output_type == "spirvtext") {
translator = std::make_unique<SpirvShaderTranslator>();
translator = std::make_unique<SpirvShaderTranslator>(spirv_features);
} else if (cvars::shader_output_type == "dxbc" ||
cvars::shader_output_type == "dxbctext") {
translator = std::make_unique<DxbcShaderTranslator>(
@ -161,7 +162,7 @@ int shader_compiler_main(const std::vector<std::string>& args) {
spv::Disassemble(spirv_disasm_stream, spirv_source);
spirv_disasm = std::move(spirv_disasm_stream.str());
ui::vulkan::SpirvToolsContext spirv_tools_context;
if (spirv_tools_context.Initialize()) {
if (spirv_tools_context.Initialize(spirv_features.spirv_version)) {
std::string spirv_validation_error;
spirv_tools_context.Validate(
reinterpret_cast<const uint32_t*>(spirv_source.data()),

View File

@ -20,10 +20,34 @@
namespace xe {
namespace gpu {
SpirvShaderTranslator::SpirvShaderTranslator(bool supports_clip_distance,
bool supports_cull_distance)
: supports_clip_distance_(supports_clip_distance),
supports_cull_distance_(supports_cull_distance) {}
SpirvShaderTranslator::Features::Features(bool all)
: spirv_version(all ? spv::Spv_1_5 : spv::Spv_1_0),
clip_distance(all),
cull_distance(all),
float_controls(all) {}
SpirvShaderTranslator::Features::Features(
const ui::vulkan::VulkanProvider& provider)
: clip_distance(provider.device_features().shaderClipDistance),
cull_distance(provider.device_features().shaderCullDistance) {
uint32_t device_version = provider.device_properties().apiVersion;
const ui::vulkan::VulkanProvider::DeviceExtensions& device_extensions =
provider.device_extensions();
if (device_version >= VK_MAKE_VERSION(1, 2, 0)) {
spirv_version = spv::Spv_1_5;
} else if (device_extensions.khr_spirv_1_4) {
spirv_version = spv::Spv_1_4;
} else if (device_version >= VK_MAKE_VERSION(1, 1, 0)) {
spirv_version = spv::Spv_1_3;
} else {
spirv_version = spv::Spv_1_0;
}
float_controls = spirv_version >= spv::Spv_1_4 ||
device_extensions.khr_shader_float_controls;
}
SpirvShaderTranslator::SpirvShaderTranslator(const Features& features)
: features_(features) {}
void SpirvShaderTranslator::Reset() {
ShaderTranslator::Reset();
@ -32,6 +56,7 @@ void SpirvShaderTranslator::Reset() {
uniform_float_constants_ = spv::NoResult;
main_interface_.clear();
var_main_registers_ = spv::NoResult;
main_switch_op_.reset();
@ -45,10 +70,16 @@ void SpirvShaderTranslator::StartTranslation() {
// Tool ID 26 "Xenia Emulator Microcode Translator".
// https://github.com/KhronosGroup/SPIRV-Headers/blob/c43a43c7cc3af55910b9bec2a71e3e8a622443cf/include/spirv/spir-v.xml#L79
// TODO(Triang3l): Logger.
builder_ = std::make_unique<spv::Builder>(1 << 16, (26 << 16) | 1, nullptr);
builder_ = std::make_unique<spv::Builder>(features_.spirv_version,
(26 << 16) | 1, nullptr);
builder_->addCapability(IsSpirvTessEvalShader() ? spv::CapabilityTessellation
: spv::CapabilityShader);
if (features_.spirv_version < spv::Spv_1_4) {
if (features_.float_controls) {
builder_->addExtension("SPV_KHR_float_controls");
}
}
ext_inst_glsl_std_450_ = builder_->import("GLSL.std.450");
builder_->setMemoryModel(spv::AddressingModelLogical,
spv::MemoryModelGLSL450);
@ -133,6 +164,9 @@ void SpirvShaderTranslator::StartTranslation() {
: kDescriptorSetFloatConstantsVertex));
builder_->addDecoration(uniform_float_constants_, spv::DecorationBinding,
0);
if (features_.spirv_version >= spv::Spv_1_4) {
main_interface_.push_back(uniform_float_constants_);
}
}
// Common uniform buffer - bool and loop constants.
@ -168,6 +202,9 @@ void SpirvShaderTranslator::StartTranslation() {
int(kDescriptorSetBoolLoopConstants));
builder_->addDecoration(uniform_bool_loop_constants_, spv::DecorationBinding,
0);
if (features_.spirv_version >= spv::Spv_1_4) {
main_interface_.push_back(uniform_bool_loop_constants_);
}
if (IsSpirvVertexOrTessEvalShader()) {
StartVertexOrTessEvalShaderBeforeMain();
@ -364,11 +401,23 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
? spv::ExecutionModelTessellationEvaluation
: spv::ExecutionModelVertex;
}
if (features_.float_controls) {
// Flush to zero, similar to the real hardware, also for things like Shader
// Model 3 multiplication emulation.
builder_->addCapability(spv::CapabilityDenormFlushToZero);
builder_->addExecutionMode(function_main_,
spv::ExecutionModeDenormFlushToZero, 32);
// Signed zero used to get VFACE from ps_param_gen, also special behavior
// for infinity in certain instructions (such as logarithm, reciprocal,
// muls_prev2).
builder_->addCapability(spv::CapabilitySignedZeroInfNanPreserve);
builder_->addExecutionMode(function_main_,
spv::ExecutionModeSignedZeroInfNanPreserve, 32);
}
spv::Instruction* entry_point =
builder_->addEntryPoint(execution_model, function_main_, "main");
if (IsSpirvVertexOrTessEvalShader()) {
CompleteVertexOrTessEvalShaderAfterMain(entry_point);
for (spv::Id interface_id : main_interface_) {
entry_point->addIdOperand(interface_id);
}
// TODO(Triang3l): Avoid copy?
@ -721,11 +770,13 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_PrimitiveID");
builder_->addDecoration(input_primitive_id_, spv::DecorationBuiltIn,
spv::BuiltInPrimitiveId);
main_interface_.push_back(input_primitive_id_);
} else {
input_vertex_index_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_VertexIndex");
builder_->addDecoration(input_vertex_index_, spv::DecorationBuiltIn,
spv::BuiltInVertexIndex);
main_interface_.push_back(input_vertex_index_);
}
// Create the entire GLSL 4.50 gl_PerVertex output similar to what glslang
@ -733,10 +784,10 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
// ClipDistance and CullDistance may exist even if the device doesn't support
// them, as long as the capabilities aren't enabled, and nothing is stored to
// them.
if (supports_clip_distance_) {
if (features_.clip_distance) {
builder_->addCapability(spv::CapabilityClipDistance);
}
if (supports_cull_distance_) {
if (features_.cull_distance) {
builder_->addCapability(spv::CapabilityCullDistance);
}
std::vector<spv::Id> struct_per_vertex_members;
@ -746,7 +797,7 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
// TODO(Triang3l): Specialization constant for ucp_cull_only_ena, for 6 + 1
// or 1 + 7 array sizes.
struct_per_vertex_members.push_back(builder_->makeArrayType(
type_float_, builder_->makeUintConstant(supports_clip_distance_ ? 6 : 1),
type_float_, builder_->makeUintConstant(features_.clip_distance ? 6 : 1),
0));
struct_per_vertex_members.push_back(
builder_->makeArrayType(type_float_, builder_->makeUintConstant(1), 0));
@ -777,6 +828,7 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
output_per_vertex_ =
builder_->createVariable(spv::NoPrecision, spv::StorageClassOutput,
type_struct_per_vertex, "xe_out_gl_PerVertex");
main_interface_.push_back(output_per_vertex_);
}
void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() {
@ -787,16 +839,6 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() {
void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {}
void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderAfterMain(
spv::Instruction* entry_point) {
if (IsSpirvTessEvalShader()) {
entry_point->addIdOperand(input_primitive_id_);
} else {
entry_point->addIdOperand(input_vertex_index_);
}
entry_point->addIdOperand(output_per_vertex_);
}
void SpirvShaderTranslator::UpdateExecConditionals(
ParsedExecInstruction::Type type, uint32_t bool_constant_index,
bool condition) {

View File

@ -17,6 +17,7 @@
#include "third_party/glslang/SPIRV/SpvBuilder.h"
#include "xenia/gpu/shader_translator.h"
#include "xenia/ui/vulkan/vulkan_provider.h"
namespace xe {
namespace gpu {
@ -46,8 +47,16 @@ class SpirvShaderTranslator : public ShaderTranslator {
kDescriptorSetSharedMemoryAndEdram,
kDescriptorSetCount,
};
SpirvShaderTranslator(bool supports_clip_distance = true,
bool supports_cull_distance = true);
struct Features {
explicit Features(const ui::vulkan::VulkanProvider& provider);
explicit Features(bool all = false);
unsigned int spirv_version;
bool clip_distance;
bool cull_distance;
bool float_controls;
};
SpirvShaderTranslator(const Features& features);
protected:
void Reset() override;
@ -90,7 +99,6 @@ class SpirvShaderTranslator : public ShaderTranslator {
void StartVertexOrTessEvalShaderBeforeMain();
void StartVertexOrTessEvalShaderInMain();
void CompleteVertexOrTessEvalShaderInMain();
void CompleteVertexOrTessEvalShaderAfterMain(spv::Instruction* entry_point);
// Updates the current flow control condition (to be called in the beginning
// of exec and in jumps), closing the previous conditionals if needed.
@ -148,8 +156,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
spv::Id ProcessVectorAluOperation(const ParsedAluInstruction& instr,
bool& predicate_written);
bool supports_clip_distance_;
bool supports_cull_distance_;
Features features_;
std::unique_ptr<spv::Builder> builder_;
@ -223,6 +230,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
};
spv::Id output_per_vertex_;
std::vector<spv::Id> main_interface_;
spv::Function* function_main_;
// bool.
spv::Id var_main_predicate_;

View File

@ -26,7 +26,7 @@ namespace xe {
namespace ui {
namespace vulkan {
bool SpirvToolsContext::Initialize() {
bool SpirvToolsContext::Initialize(unsigned int spirv_version) {
const char* vulkan_sdk_env = std::getenv("VULKAN_SDK");
if (!vulkan_sdk_env) {
XELOGE("SPIRV-Tools: Failed to get the VULKAN_SDK environment variable");
@ -63,7 +63,17 @@ bool SpirvToolsContext::Initialize() {
Shutdown();
return false;
}
context_ = fn_spvContextCreate_(SPV_ENV_VULKAN_1_0);
spv_target_env target_env;
if (spirv_version >= 0x10500) {
target_env = SPV_ENV_VULKAN_1_2;
} else if (spirv_version >= 0x10400) {
target_env = SPV_ENV_VULKAN_1_1_SPIRV_1_4;
} else if (spirv_version >= 0x10300) {
target_env = SPV_ENV_VULKAN_1_1;
} else {
target_env = SPV_ENV_VULKAN_1_0;
}
context_ = fn_spvContextCreate_(target_env);
if (!context_) {
XELOGE("SPIRV-Tools: Failed to create a Vulkan 1.0 context");
Shutdown();

View File

@ -32,7 +32,7 @@ class SpirvToolsContext {
SpirvToolsContext(const SpirvToolsContext& context) = delete;
SpirvToolsContext& operator=(const SpirvToolsContext& context) = delete;
~SpirvToolsContext() { Shutdown(); }
bool Initialize();
bool Initialize(unsigned int spirv_version);
void Shutdown();
spv_result_t Validate(const uint32_t* words, size_t num_words,

View File

@ -392,6 +392,10 @@ bool VulkanProvider::Initialize() {
std::memset(&device_extensions_, 0, sizeof(device_extensions_));
if (device_properties_.apiVersion >= VK_MAKE_VERSION(1, 1, 0)) {
device_extensions_.khr_dedicated_allocation = true;
if (device_properties_.apiVersion >= VK_MAKE_VERSION(1, 2, 0)) {
device_extensions_.khr_shader_float_controls = true;
device_extensions_.khr_spirv_1_4 = true;
}
}
bool device_supports_swapchain = false;
for (const VkExtensionProperties& device_extension :
@ -405,6 +409,13 @@ bool VulkanProvider::Initialize() {
!std::strcmp(device_extension_name,
"VK_KHR_dedicated_allocation")) {
device_extensions_.khr_dedicated_allocation = true;
} else if (!device_extensions_.khr_shader_float_controls &&
!std::strcmp(device_extension_name,
"VK_KHR_shader_float_controls")) {
device_extensions_.khr_shader_float_controls = true;
} else if (!device_extensions_.khr_spirv_1_4 &&
!std::strcmp(device_extension_name, "VK_KHR_spirv_1_4")) {
device_extensions_.khr_spirv_1_4 = true;
} else if (!device_supports_swapchain &&
!std::strcmp(device_extension_name, "VK_KHR_swapchain")) {
device_supports_swapchain = true;
@ -466,6 +477,10 @@ bool VulkanProvider::Initialize() {
device_extensions_.ext_fragment_shader_interlock ? "yes" : "no");
XELOGVK("* VK_KHR_dedicated_allocation: {}",
device_extensions_.khr_dedicated_allocation ? "yes" : "no");
XELOGVK("* VK_KHR_shader_float_controls: {}",
device_extensions_.khr_shader_float_controls ? "yes" : "no");
XELOGVK("* VK_KHR_spirv_1_4: {}",
device_extensions_.khr_spirv_1_4 ? "yes" : "no");
// TODO(Triang3l): Report properties, features.
// Create the device.
@ -493,9 +508,17 @@ bool VulkanProvider::Initialize() {
if (device_extensions_.ext_fragment_shader_interlock) {
device_extensions_enabled.push_back("VK_EXT_fragment_shader_interlock");
}
if (device_properties_.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
if (device_extensions_.khr_dedicated_allocation) {
device_extensions_enabled.push_back("VK_KHR_dedicated_allocation");
if (device_properties_.apiVersion < VK_MAKE_VERSION(1, 2, 0)) {
if (device_properties_.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
if (device_extensions_.khr_dedicated_allocation) {
device_extensions_enabled.push_back("VK_KHR_dedicated_allocation");
}
}
if (device_extensions_.khr_shader_float_controls) {
device_extensions_enabled.push_back("VK_KHR_shader_float_controls");
}
if (device_extensions_.khr_spirv_1_4) {
device_extensions_enabled.push_back("VK_KHR_spirv_1_4");
}
}
VkDeviceCreateInfo device_create_info;

View File

@ -104,6 +104,10 @@ class VulkanProvider : public GraphicsProvider {
bool ext_fragment_shader_interlock;
// Core since 1.1.0.
bool khr_dedicated_allocation;
// Core since 1.2.0.
bool khr_shader_float_controls;
// Core since 1.2.0.
bool khr_spirv_1_4;
};
const DeviceExtensions& device_extensions() const {
return device_extensions_;