[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()); shader_type, ucode_data_hash, ucode_dwords.data(), ucode_dwords.size());
std::unique_ptr<ShaderTranslator> translator; std::unique_ptr<ShaderTranslator> translator;
SpirvShaderTranslator::Features spirv_features(true);
if (cvars::shader_output_type == "spirv" || if (cvars::shader_output_type == "spirv" ||
cvars::shader_output_type == "spirvtext") { cvars::shader_output_type == "spirvtext") {
translator = std::make_unique<SpirvShaderTranslator>(); translator = std::make_unique<SpirvShaderTranslator>(spirv_features);
} else if (cvars::shader_output_type == "dxbc" || } else if (cvars::shader_output_type == "dxbc" ||
cvars::shader_output_type == "dxbctext") { cvars::shader_output_type == "dxbctext") {
translator = std::make_unique<DxbcShaderTranslator>( 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); spv::Disassemble(spirv_disasm_stream, spirv_source);
spirv_disasm = std::move(spirv_disasm_stream.str()); spirv_disasm = std::move(spirv_disasm_stream.str());
ui::vulkan::SpirvToolsContext spirv_tools_context; 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; std::string spirv_validation_error;
spirv_tools_context.Validate( spirv_tools_context.Validate(
reinterpret_cast<const uint32_t*>(spirv_source.data()), reinterpret_cast<const uint32_t*>(spirv_source.data()),

View File

@ -20,10 +20,34 @@
namespace xe { namespace xe {
namespace gpu { namespace gpu {
SpirvShaderTranslator::SpirvShaderTranslator(bool supports_clip_distance, SpirvShaderTranslator::Features::Features(bool all)
bool supports_cull_distance) : spirv_version(all ? spv::Spv_1_5 : spv::Spv_1_0),
: supports_clip_distance_(supports_clip_distance), clip_distance(all),
supports_cull_distance_(supports_cull_distance) {} 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() { void SpirvShaderTranslator::Reset() {
ShaderTranslator::Reset(); ShaderTranslator::Reset();
@ -32,6 +56,7 @@ void SpirvShaderTranslator::Reset() {
uniform_float_constants_ = spv::NoResult; uniform_float_constants_ = spv::NoResult;
main_interface_.clear();
var_main_registers_ = spv::NoResult; var_main_registers_ = spv::NoResult;
main_switch_op_.reset(); main_switch_op_.reset();
@ -45,10 +70,16 @@ void SpirvShaderTranslator::StartTranslation() {
// Tool ID 26 "Xenia Emulator Microcode Translator". // Tool ID 26 "Xenia Emulator Microcode Translator".
// https://github.com/KhronosGroup/SPIRV-Headers/blob/c43a43c7cc3af55910b9bec2a71e3e8a622443cf/include/spirv/spir-v.xml#L79 // https://github.com/KhronosGroup/SPIRV-Headers/blob/c43a43c7cc3af55910b9bec2a71e3e8a622443cf/include/spirv/spir-v.xml#L79
// TODO(Triang3l): Logger. // 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 builder_->addCapability(IsSpirvTessEvalShader() ? spv::CapabilityTessellation
: spv::CapabilityShader); : 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"); ext_inst_glsl_std_450_ = builder_->import("GLSL.std.450");
builder_->setMemoryModel(spv::AddressingModelLogical, builder_->setMemoryModel(spv::AddressingModelLogical,
spv::MemoryModelGLSL450); spv::MemoryModelGLSL450);
@ -133,6 +164,9 @@ void SpirvShaderTranslator::StartTranslation() {
: kDescriptorSetFloatConstantsVertex)); : kDescriptorSetFloatConstantsVertex));
builder_->addDecoration(uniform_float_constants_, spv::DecorationBinding, builder_->addDecoration(uniform_float_constants_, spv::DecorationBinding,
0); 0);
if (features_.spirv_version >= spv::Spv_1_4) {
main_interface_.push_back(uniform_float_constants_);
}
} }
// Common uniform buffer - bool and loop constants. // Common uniform buffer - bool and loop constants.
@ -168,6 +202,9 @@ void SpirvShaderTranslator::StartTranslation() {
int(kDescriptorSetBoolLoopConstants)); int(kDescriptorSetBoolLoopConstants));
builder_->addDecoration(uniform_bool_loop_constants_, spv::DecorationBinding, builder_->addDecoration(uniform_bool_loop_constants_, spv::DecorationBinding,
0); 0);
if (features_.spirv_version >= spv::Spv_1_4) {
main_interface_.push_back(uniform_bool_loop_constants_);
}
if (IsSpirvVertexOrTessEvalShader()) { if (IsSpirvVertexOrTessEvalShader()) {
StartVertexOrTessEvalShaderBeforeMain(); StartVertexOrTessEvalShaderBeforeMain();
@ -364,11 +401,23 @@ std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
? spv::ExecutionModelTessellationEvaluation ? spv::ExecutionModelTessellationEvaluation
: spv::ExecutionModelVertex; : 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 = spv::Instruction* entry_point =
builder_->addEntryPoint(execution_model, function_main_, "main"); builder_->addEntryPoint(execution_model, function_main_, "main");
for (spv::Id interface_id : main_interface_) {
if (IsSpirvVertexOrTessEvalShader()) { entry_point->addIdOperand(interface_id);
CompleteVertexOrTessEvalShaderAfterMain(entry_point);
} }
// TODO(Triang3l): Avoid copy? // TODO(Triang3l): Avoid copy?
@ -721,11 +770,13 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_PrimitiveID"); spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_PrimitiveID");
builder_->addDecoration(input_primitive_id_, spv::DecorationBuiltIn, builder_->addDecoration(input_primitive_id_, spv::DecorationBuiltIn,
spv::BuiltInPrimitiveId); spv::BuiltInPrimitiveId);
main_interface_.push_back(input_primitive_id_);
} else { } else {
input_vertex_index_ = builder_->createVariable( input_vertex_index_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_VertexIndex"); spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_VertexIndex");
builder_->addDecoration(input_vertex_index_, spv::DecorationBuiltIn, builder_->addDecoration(input_vertex_index_, spv::DecorationBuiltIn,
spv::BuiltInVertexIndex); spv::BuiltInVertexIndex);
main_interface_.push_back(input_vertex_index_);
} }
// Create the entire GLSL 4.50 gl_PerVertex output similar to what glslang // 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 // 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, as long as the capabilities aren't enabled, and nothing is stored to
// them. // them.
if (supports_clip_distance_) { if (features_.clip_distance) {
builder_->addCapability(spv::CapabilityClipDistance); builder_->addCapability(spv::CapabilityClipDistance);
} }
if (supports_cull_distance_) { if (features_.cull_distance) {
builder_->addCapability(spv::CapabilityCullDistance); builder_->addCapability(spv::CapabilityCullDistance);
} }
std::vector<spv::Id> struct_per_vertex_members; 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 // TODO(Triang3l): Specialization constant for ucp_cull_only_ena, for 6 + 1
// or 1 + 7 array sizes. // or 1 + 7 array sizes.
struct_per_vertex_members.push_back(builder_->makeArrayType( 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)); 0));
struct_per_vertex_members.push_back( struct_per_vertex_members.push_back(
builder_->makeArrayType(type_float_, builder_->makeUintConstant(1), 0)); builder_->makeArrayType(type_float_, builder_->makeUintConstant(1), 0));
@ -777,6 +828,7 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
output_per_vertex_ = output_per_vertex_ =
builder_->createVariable(spv::NoPrecision, spv::StorageClassOutput, builder_->createVariable(spv::NoPrecision, spv::StorageClassOutput,
type_struct_per_vertex, "xe_out_gl_PerVertex"); type_struct_per_vertex, "xe_out_gl_PerVertex");
main_interface_.push_back(output_per_vertex_);
} }
void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() { void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() {
@ -787,16 +839,6 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() {
void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {} 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( void SpirvShaderTranslator::UpdateExecConditionals(
ParsedExecInstruction::Type type, uint32_t bool_constant_index, ParsedExecInstruction::Type type, uint32_t bool_constant_index,
bool condition) { bool condition) {

View File

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

View File

@ -26,7 +26,7 @@ namespace xe {
namespace ui { namespace ui {
namespace vulkan { namespace vulkan {
bool SpirvToolsContext::Initialize() { bool SpirvToolsContext::Initialize(unsigned int spirv_version) {
const char* vulkan_sdk_env = std::getenv("VULKAN_SDK"); const char* vulkan_sdk_env = std::getenv("VULKAN_SDK");
if (!vulkan_sdk_env) { if (!vulkan_sdk_env) {
XELOGE("SPIRV-Tools: Failed to get the VULKAN_SDK environment variable"); XELOGE("SPIRV-Tools: Failed to get the VULKAN_SDK environment variable");
@ -63,7 +63,17 @@ bool SpirvToolsContext::Initialize() {
Shutdown(); Shutdown();
return false; 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_) { if (!context_) {
XELOGE("SPIRV-Tools: Failed to create a Vulkan 1.0 context"); XELOGE("SPIRV-Tools: Failed to create a Vulkan 1.0 context");
Shutdown(); Shutdown();

View File

@ -32,7 +32,7 @@ class SpirvToolsContext {
SpirvToolsContext(const SpirvToolsContext& context) = delete; SpirvToolsContext(const SpirvToolsContext& context) = delete;
SpirvToolsContext& operator=(const SpirvToolsContext& context) = delete; SpirvToolsContext& operator=(const SpirvToolsContext& context) = delete;
~SpirvToolsContext() { Shutdown(); } ~SpirvToolsContext() { Shutdown(); }
bool Initialize(); bool Initialize(unsigned int spirv_version);
void Shutdown(); void Shutdown();
spv_result_t Validate(const uint32_t* words, size_t num_words, 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_)); std::memset(&device_extensions_, 0, sizeof(device_extensions_));
if (device_properties_.apiVersion >= VK_MAKE_VERSION(1, 1, 0)) { if (device_properties_.apiVersion >= VK_MAKE_VERSION(1, 1, 0)) {
device_extensions_.khr_dedicated_allocation = true; 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; bool device_supports_swapchain = false;
for (const VkExtensionProperties& device_extension : for (const VkExtensionProperties& device_extension :
@ -405,6 +409,13 @@ bool VulkanProvider::Initialize() {
!std::strcmp(device_extension_name, !std::strcmp(device_extension_name,
"VK_KHR_dedicated_allocation")) { "VK_KHR_dedicated_allocation")) {
device_extensions_.khr_dedicated_allocation = true; 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 && } else if (!device_supports_swapchain &&
!std::strcmp(device_extension_name, "VK_KHR_swapchain")) { !std::strcmp(device_extension_name, "VK_KHR_swapchain")) {
device_supports_swapchain = true; device_supports_swapchain = true;
@ -466,6 +477,10 @@ bool VulkanProvider::Initialize() {
device_extensions_.ext_fragment_shader_interlock ? "yes" : "no"); device_extensions_.ext_fragment_shader_interlock ? "yes" : "no");
XELOGVK("* VK_KHR_dedicated_allocation: {}", XELOGVK("* VK_KHR_dedicated_allocation: {}",
device_extensions_.khr_dedicated_allocation ? "yes" : "no"); 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. // TODO(Triang3l): Report properties, features.
// Create the device. // Create the device.
@ -493,9 +508,17 @@ bool VulkanProvider::Initialize() {
if (device_extensions_.ext_fragment_shader_interlock) { if (device_extensions_.ext_fragment_shader_interlock) {
device_extensions_enabled.push_back("VK_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_properties_.apiVersion < VK_MAKE_VERSION(1, 2, 0)) {
if (device_extensions_.khr_dedicated_allocation) { if (device_properties_.apiVersion < VK_MAKE_VERSION(1, 1, 0)) {
device_extensions_enabled.push_back("VK_KHR_dedicated_allocation"); 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; VkDeviceCreateInfo device_create_info;

View File

@ -104,6 +104,10 @@ class VulkanProvider : public GraphicsProvider {
bool ext_fragment_shader_interlock; bool ext_fragment_shader_interlock;
// Core since 1.1.0. // Core since 1.1.0.
bool khr_dedicated_allocation; 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 { const DeviceExtensions& device_extensions() const {
return device_extensions_; return device_extensions_;