2020-10-11 17:22:15 +00:00
|
|
|
/**
|
|
|
|
******************************************************************************
|
|
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
|
|
******************************************************************************
|
|
|
|
* Copyright 2020 Ben Vanik. All rights reserved. *
|
|
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
|
|
******************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "xenia/gpu/spirv_shader_translator.h"
|
|
|
|
|
2020-10-29 19:07:02 +00:00
|
|
|
#include <algorithm>
|
2020-11-03 20:31:52 +00:00
|
|
|
#include <cstddef>
|
2020-10-11 17:22:15 +00:00
|
|
|
#include <memory>
|
2020-10-16 16:55:41 +00:00
|
|
|
#include <utility>
|
2020-10-11 17:22:15 +00:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "third_party/glslang/SPIRV/GLSL.std.450.h"
|
2020-10-16 16:55:41 +00:00
|
|
|
#include "xenia/base/assert.h"
|
2020-11-03 20:31:52 +00:00
|
|
|
#include "xenia/base/math.h"
|
2020-10-11 17:22:15 +00:00
|
|
|
|
|
|
|
namespace xe {
|
|
|
|
namespace gpu {
|
|
|
|
|
2020-10-31 13:22:15 +00:00
|
|
|
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) {}
|
2020-10-11 17:22:15 +00:00
|
|
|
|
|
|
|
void SpirvShaderTranslator::Reset() {
|
|
|
|
ShaderTranslator::Reset();
|
|
|
|
|
|
|
|
builder_.reset();
|
2020-10-16 16:55:41 +00:00
|
|
|
|
2020-10-27 19:48:47 +00:00
|
|
|
uniform_float_constants_ = spv::NoResult;
|
|
|
|
|
2020-10-31 13:22:15 +00:00
|
|
|
main_interface_.clear();
|
2020-10-27 19:48:47 +00:00
|
|
|
var_main_registers_ = spv::NoResult;
|
|
|
|
|
2020-10-18 12:36:02 +00:00
|
|
|
main_switch_op_.reset();
|
|
|
|
main_switch_next_pc_phi_operands_.clear();
|
2020-10-25 12:09:39 +00:00
|
|
|
|
|
|
|
cf_exec_conditional_merge_ = nullptr;
|
|
|
|
cf_instruction_predicate_merge_ = nullptr;
|
2020-10-11 17:22:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SpirvShaderTranslator::StartTranslation() {
|
2020-10-16 16:55:41 +00:00
|
|
|
// Tool ID 26 "Xenia Emulator Microcode Translator".
|
|
|
|
// https://github.com/KhronosGroup/SPIRV-Headers/blob/c43a43c7cc3af55910b9bec2a71e3e8a622443cf/include/spirv/spir-v.xml#L79
|
2020-10-11 17:22:15 +00:00
|
|
|
// TODO(Triang3l): Logger.
|
2020-10-31 13:22:15 +00:00
|
|
|
builder_ = std::make_unique<spv::Builder>(features_.spirv_version,
|
|
|
|
(26 << 16) | 1, nullptr);
|
2020-10-11 17:22:15 +00:00
|
|
|
|
|
|
|
builder_->addCapability(IsSpirvTessEvalShader() ? spv::CapabilityTessellation
|
|
|
|
: spv::CapabilityShader);
|
2020-10-31 13:22:15 +00:00
|
|
|
if (features_.spirv_version < spv::Spv_1_4) {
|
|
|
|
if (features_.float_controls) {
|
|
|
|
builder_->addExtension("SPV_KHR_float_controls");
|
|
|
|
}
|
|
|
|
}
|
2020-10-11 17:22:15 +00:00
|
|
|
ext_inst_glsl_std_450_ = builder_->import("GLSL.std.450");
|
|
|
|
builder_->setMemoryModel(spv::AddressingModelLogical,
|
|
|
|
spv::MemoryModelGLSL450);
|
|
|
|
builder_->setSource(spv::SourceLanguageUnknown, 0);
|
|
|
|
|
|
|
|
type_void_ = builder_->makeVoidType();
|
2020-10-16 16:55:41 +00:00
|
|
|
type_bool_ = builder_->makeBoolType();
|
2020-10-31 14:56:46 +00:00
|
|
|
type_bool2_ = builder_->makeVectorType(type_bool_, 2);
|
|
|
|
type_bool3_ = builder_->makeVectorType(type_bool_, 3);
|
|
|
|
type_bool4_ = builder_->makeVectorType(type_bool_, 4);
|
2020-10-16 16:55:41 +00:00
|
|
|
type_int_ = builder_->makeIntType(32);
|
|
|
|
type_int4_ = builder_->makeVectorType(type_int_, 4);
|
2020-10-25 12:09:39 +00:00
|
|
|
type_uint_ = builder_->makeUintType(32);
|
2020-10-25 17:24:48 +00:00
|
|
|
type_uint3_ = builder_->makeVectorType(type_uint_, 3);
|
2020-10-25 12:09:39 +00:00
|
|
|
type_uint4_ = builder_->makeVectorType(type_uint_, 4);
|
2020-10-11 17:22:15 +00:00
|
|
|
type_float_ = builder_->makeFloatType(32);
|
|
|
|
type_float2_ = builder_->makeVectorType(type_float_, 2);
|
|
|
|
type_float3_ = builder_->makeVectorType(type_float_, 3);
|
|
|
|
type_float4_ = builder_->makeVectorType(type_float_, 4);
|
2020-10-16 16:55:41 +00:00
|
|
|
|
|
|
|
const_int_0_ = builder_->makeIntConstant(0);
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(4);
|
|
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
|
|
id_vector_temp_.push_back(const_int_0_);
|
|
|
|
}
|
|
|
|
const_int4_0_ = builder_->makeCompositeConstant(type_int4_, id_vector_temp_);
|
2020-10-25 17:24:48 +00:00
|
|
|
const_uint_0_ = builder_->makeUintConstant(0);
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(4);
|
|
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
|
|
id_vector_temp_.push_back(const_uint_0_);
|
|
|
|
}
|
|
|
|
const_uint4_0_ =
|
|
|
|
builder_->makeCompositeConstant(type_uint4_, id_vector_temp_);
|
2020-10-16 16:55:41 +00:00
|
|
|
const_float_0_ = builder_->makeFloatConstant(0.0f);
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(4);
|
2020-10-29 19:07:02 +00:00
|
|
|
id_vector_temp_.push_back(const_float_0_);
|
|
|
|
for (uint32_t i = 1; i < 4; ++i) {
|
2020-10-16 16:55:41 +00:00
|
|
|
id_vector_temp_.push_back(const_float_0_);
|
2020-10-29 19:07:02 +00:00
|
|
|
const_float_vectors_0_[i] = builder_->makeCompositeConstant(
|
|
|
|
type_float_vectors_[i], id_vector_temp_);
|
|
|
|
}
|
|
|
|
const_float_1_ = builder_->makeFloatConstant(1.0f);
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(4);
|
|
|
|
id_vector_temp_.push_back(const_float_1_);
|
|
|
|
for (uint32_t i = 1; i < 4; ++i) {
|
|
|
|
id_vector_temp_.push_back(const_float_1_);
|
|
|
|
const_float_vectors_1_[i] = builder_->makeCompositeConstant(
|
|
|
|
type_float_vectors_[i], id_vector_temp_);
|
2020-10-16 16:55:41 +00:00
|
|
|
}
|
2020-10-29 19:07:02 +00:00
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(2);
|
|
|
|
id_vector_temp_.push_back(const_float_0_);
|
|
|
|
id_vector_temp_.push_back(const_float_1_);
|
|
|
|
const_float2_0_1_ =
|
|
|
|
builder_->makeCompositeConstant(type_float2_, id_vector_temp_);
|
2020-10-11 17:22:15 +00:00
|
|
|
|
2020-11-03 20:31:52 +00:00
|
|
|
// Common uniform buffer - system constants.
|
|
|
|
struct SystemConstant {
|
|
|
|
const char* name;
|
|
|
|
size_t offset;
|
|
|
|
spv::Id type;
|
|
|
|
};
|
|
|
|
const SystemConstant system_constants[] = {
|
|
|
|
{"vertex_index_endian", offsetof(SystemConstants, vertex_index_endian),
|
|
|
|
type_uint_},
|
|
|
|
{"vertex_base_index", offsetof(SystemConstants, vertex_base_index),
|
|
|
|
type_int_},
|
|
|
|
};
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(xe::countof(system_constants));
|
|
|
|
for (size_t i = 0; i < xe::countof(system_constants); ++i) {
|
|
|
|
id_vector_temp_.push_back(system_constants[i].type);
|
|
|
|
}
|
|
|
|
spv::Id type_system_constants =
|
|
|
|
builder_->makeStructType(id_vector_temp_, "XeSystemConstants");
|
|
|
|
for (size_t i = 0; i < xe::countof(system_constants); ++i) {
|
|
|
|
const SystemConstant& system_constant = system_constants[i];
|
|
|
|
builder_->addMemberName(type_system_constants, static_cast<unsigned int>(i),
|
|
|
|
system_constant.name);
|
|
|
|
builder_->addMemberDecoration(
|
|
|
|
type_system_constants, static_cast<unsigned int>(i),
|
|
|
|
spv::DecorationOffset, int(system_constant.offset));
|
|
|
|
}
|
|
|
|
builder_->addDecoration(type_system_constants, spv::DecorationBlock);
|
|
|
|
uniform_system_constants_ = builder_->createVariable(
|
|
|
|
spv::NoPrecision, spv::StorageClassUniform, type_system_constants,
|
|
|
|
"xe_uniform_system_constants");
|
|
|
|
builder_->addDecoration(uniform_system_constants_,
|
|
|
|
spv::DecorationDescriptorSet,
|
|
|
|
kDescriptorSetSystemConstants);
|
|
|
|
builder_->addDecoration(uniform_system_constants_, spv::DecorationBinding, 0);
|
|
|
|
if (features_.spirv_version >= spv::Spv_1_4) {
|
|
|
|
main_interface_.push_back(uniform_system_constants_);
|
|
|
|
}
|
|
|
|
|
2020-10-27 19:48:47 +00:00
|
|
|
// Common uniform buffer - float constants.
|
|
|
|
uint32_t float_constant_count = constant_register_map().float_count;
|
|
|
|
if (float_constant_count) {
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.push_back(builder_->makeArrayType(
|
|
|
|
type_float4_, builder_->makeUintConstant(float_constant_count),
|
|
|
|
sizeof(float) * 4));
|
|
|
|
// Currently (as of October 24, 2020) makeArrayType only uses the stride to
|
|
|
|
// check if deduplication can be done - the array stride decoration needs to
|
|
|
|
// be applied explicitly.
|
|
|
|
builder_->addDecoration(id_vector_temp_.back(), spv::DecorationArrayStride,
|
|
|
|
sizeof(float) * 4);
|
|
|
|
spv::Id type_float_constants =
|
|
|
|
builder_->makeStructType(id_vector_temp_, "XeFloatConstants");
|
|
|
|
builder_->addMemberName(type_float_constants, 0, "float_constants");
|
|
|
|
builder_->addMemberDecoration(type_float_constants, 0,
|
|
|
|
spv::DecorationOffset, 0);
|
|
|
|
builder_->addDecoration(type_float_constants, spv::DecorationBlock);
|
|
|
|
uniform_float_constants_ = builder_->createVariable(
|
|
|
|
spv::NoPrecision, spv::StorageClassUniform, type_float_constants,
|
|
|
|
"xe_uniform_float_constants");
|
|
|
|
builder_->addDecoration(
|
|
|
|
uniform_float_constants_, spv::DecorationDescriptorSet,
|
|
|
|
int(IsSpirvFragmentShader() ? kDescriptorSetFloatConstantsPixel
|
|
|
|
: kDescriptorSetFloatConstantsVertex));
|
|
|
|
builder_->addDecoration(uniform_float_constants_, spv::DecorationBinding,
|
|
|
|
0);
|
2020-10-31 13:22:15 +00:00
|
|
|
if (features_.spirv_version >= spv::Spv_1_4) {
|
|
|
|
main_interface_.push_back(uniform_float_constants_);
|
|
|
|
}
|
2020-10-27 19:48:47 +00:00
|
|
|
}
|
|
|
|
|
2020-10-25 12:09:39 +00:00
|
|
|
// Common uniform buffer - bool and loop constants.
|
2020-10-29 19:07:02 +00:00
|
|
|
// Uniform buffers must have std140 packing, so using arrays of 4-component
|
|
|
|
// vectors instead of scalar arrays because the latter would have padding to
|
|
|
|
// 16 bytes in each element.
|
2020-10-25 12:09:39 +00:00
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(2);
|
|
|
|
// 256 bool constants.
|
|
|
|
id_vector_temp_.push_back(builder_->makeArrayType(
|
|
|
|
type_uint4_, builder_->makeUintConstant(2), sizeof(uint32_t) * 4));
|
|
|
|
builder_->addDecoration(id_vector_temp_.back(), spv::DecorationArrayStride,
|
|
|
|
sizeof(uint32_t) * 4);
|
|
|
|
// 32 loop constants.
|
|
|
|
id_vector_temp_.push_back(builder_->makeArrayType(
|
|
|
|
type_uint4_, builder_->makeUintConstant(8), sizeof(uint32_t) * 4));
|
|
|
|
builder_->addDecoration(id_vector_temp_.back(), spv::DecorationArrayStride,
|
|
|
|
sizeof(uint32_t) * 4);
|
|
|
|
spv::Id type_bool_loop_constants =
|
|
|
|
builder_->makeStructType(id_vector_temp_, "XeBoolLoopConstants");
|
|
|
|
builder_->addMemberName(type_bool_loop_constants, 0, "bool_constants");
|
|
|
|
builder_->addMemberDecoration(type_bool_loop_constants, 0,
|
|
|
|
spv::DecorationOffset, 0);
|
|
|
|
builder_->addMemberName(type_bool_loop_constants, 1, "loop_constants");
|
|
|
|
builder_->addMemberDecoration(type_bool_loop_constants, 1,
|
|
|
|
spv::DecorationOffset, sizeof(uint32_t) * 8);
|
|
|
|
builder_->addDecoration(type_bool_loop_constants, spv::DecorationBlock);
|
|
|
|
uniform_bool_loop_constants_ = builder_->createVariable(
|
|
|
|
spv::NoPrecision, spv::StorageClassUniform, type_bool_loop_constants,
|
|
|
|
"xe_uniform_bool_loop_constants");
|
|
|
|
builder_->addDecoration(uniform_bool_loop_constants_,
|
|
|
|
spv::DecorationDescriptorSet,
|
|
|
|
int(kDescriptorSetBoolLoopConstants));
|
|
|
|
builder_->addDecoration(uniform_bool_loop_constants_, spv::DecorationBinding,
|
|
|
|
0);
|
2020-10-31 13:22:15 +00:00
|
|
|
if (features_.spirv_version >= spv::Spv_1_4) {
|
|
|
|
main_interface_.push_back(uniform_bool_loop_constants_);
|
|
|
|
}
|
2020-10-25 12:09:39 +00:00
|
|
|
|
2020-10-11 17:22:15 +00:00
|
|
|
if (IsSpirvVertexOrTessEvalShader()) {
|
|
|
|
StartVertexOrTessEvalShaderBeforeMain();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Begin the main function.
|
|
|
|
std::vector<spv::Id> main_param_types;
|
|
|
|
std::vector<std::vector<spv::Decoration>> main_precisions;
|
2020-10-16 16:55:41 +00:00
|
|
|
spv::Block* function_main_entry;
|
|
|
|
function_main_ = builder_->makeFunctionEntry(
|
|
|
|
spv::NoPrecision, type_void_, "main", main_param_types, main_precisions,
|
|
|
|
&function_main_entry);
|
2020-10-11 17:22:15 +00:00
|
|
|
|
2020-10-16 16:55:41 +00:00
|
|
|
// Begin ucode translation. Initialize everything, even without defined
|
|
|
|
// defaults, for safety.
|
|
|
|
var_main_predicate_ = builder_->createVariable(
|
|
|
|
spv::NoPrecision, spv::StorageClassFunction, type_bool_,
|
|
|
|
"xe_var_predicate", builder_->makeBoolConstant(false));
|
2020-10-25 17:24:48 +00:00
|
|
|
var_main_loop_count_ = builder_->createVariable(
|
|
|
|
spv::NoPrecision, spv::StorageClassFunction, type_uint4_,
|
|
|
|
"xe_var_loop_count", const_uint4_0_);
|
2020-10-16 16:55:41 +00:00
|
|
|
var_main_address_absolute_ = builder_->createVariable(
|
|
|
|
spv::NoPrecision, spv::StorageClassFunction, type_int_,
|
|
|
|
"xe_var_address_absolute", const_int_0_);
|
|
|
|
var_main_address_relative_ = builder_->createVariable(
|
|
|
|
spv::NoPrecision, spv::StorageClassFunction, type_int4_,
|
|
|
|
"xe_var_address_relative", const_int4_0_);
|
2020-11-01 17:42:59 +00:00
|
|
|
var_main_previous_scalar_ = builder_->createVariable(
|
|
|
|
spv::NoPrecision, spv::StorageClassFunction, type_float_,
|
|
|
|
"xe_var_previous_scalar", const_float_0_);
|
2020-10-16 16:55:41 +00:00
|
|
|
uint32_t register_array_size = register_count();
|
|
|
|
if (register_array_size) {
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(register_array_size);
|
|
|
|
// TODO(Triang3l): In PS, only initialize starting from the interpolators,
|
|
|
|
// probably manually. But not very important.
|
|
|
|
for (uint32_t i = 0; i < register_array_size; ++i) {
|
|
|
|
id_vector_temp_.push_back(const_float4_0_);
|
|
|
|
}
|
|
|
|
spv::Id type_register_array = builder_->makeArrayType(
|
|
|
|
type_float4_, builder_->makeUintConstant(register_array_size), 0);
|
2020-10-11 17:22:15 +00:00
|
|
|
var_main_registers_ = builder_->createVariable(
|
2020-10-16 16:55:41 +00:00
|
|
|
spv::NoPrecision, spv::StorageClassFunction, type_register_array,
|
|
|
|
"xe_var_registers",
|
|
|
|
builder_->makeCompositeConstant(type_register_array, id_vector_temp_));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write the execution model-specific prologue with access to variables in the
|
|
|
|
// main function.
|
|
|
|
if (IsSpirvVertexOrTessEvalShader()) {
|
|
|
|
StartVertexOrTessEvalShaderInMain();
|
2020-10-11 17:22:15 +00:00
|
|
|
}
|
2020-10-16 16:55:41 +00:00
|
|
|
|
|
|
|
// Open the main loop.
|
2020-10-25 12:09:39 +00:00
|
|
|
spv::Block& main_loop_pre_header = *builder_->getBuildPoint();
|
2020-10-16 16:55:41 +00:00
|
|
|
main_loop_header_ = &builder_->makeNewBlock();
|
|
|
|
spv::Block& main_loop_body = builder_->makeNewBlock();
|
|
|
|
// Added later because the body has nested control flow, but according to the
|
|
|
|
// specification:
|
|
|
|
// "The order of blocks in a function must satisfy the rule that blocks appear
|
|
|
|
// before all blocks they dominate."
|
|
|
|
main_loop_continue_ =
|
|
|
|
new spv::Block(builder_->getUniqueId(), *function_main_);
|
|
|
|
main_loop_merge_ = new spv::Block(builder_->getUniqueId(), *function_main_);
|
|
|
|
builder_->createBranch(main_loop_header_);
|
|
|
|
|
2020-10-18 12:36:02 +00:00
|
|
|
// If no jumps, don't create a switch, but still create a loop so exece can
|
|
|
|
// break.
|
|
|
|
bool has_main_switch = !label_addresses().empty();
|
|
|
|
|
2020-10-16 16:55:41 +00:00
|
|
|
// Main loop header - based on whether it's the first iteration (entered from
|
|
|
|
// the function or from the continuation), choose the program counter.
|
|
|
|
builder_->setBuildPoint(main_loop_header_);
|
2020-10-27 19:48:47 +00:00
|
|
|
spv::Id main_loop_pc_current = spv::NoResult;
|
2020-10-18 12:36:02 +00:00
|
|
|
if (has_main_switch) {
|
2020-10-25 17:24:48 +00:00
|
|
|
// OpPhi must be the first in the block.
|
2020-10-18 12:36:02 +00:00
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(4);
|
|
|
|
id_vector_temp_.push_back(const_int_0_);
|
2020-10-25 12:09:39 +00:00
|
|
|
id_vector_temp_.push_back(main_loop_pre_header.getId());
|
2020-10-18 12:36:02 +00:00
|
|
|
main_loop_pc_next_ = builder_->getUniqueId();
|
|
|
|
id_vector_temp_.push_back(main_loop_pc_next_);
|
|
|
|
id_vector_temp_.push_back(main_loop_continue_->getId());
|
|
|
|
main_loop_pc_current =
|
|
|
|
builder_->createOp(spv::OpPhi, type_int_, id_vector_temp_);
|
|
|
|
}
|
2020-10-16 16:55:41 +00:00
|
|
|
uint_vector_temp_.clear();
|
|
|
|
builder_->createLoopMerge(main_loop_merge_, main_loop_continue_,
|
|
|
|
spv::LoopControlDontUnrollMask, uint_vector_temp_);
|
|
|
|
builder_->createBranch(&main_loop_body);
|
|
|
|
|
|
|
|
// Main loop body.
|
|
|
|
builder_->setBuildPoint(&main_loop_body);
|
2020-10-18 12:36:02 +00:00
|
|
|
if (has_main_switch) {
|
|
|
|
// Create the program counter switch with cases for every label and for
|
|
|
|
// label 0.
|
|
|
|
main_switch_header_ = builder_->getBuildPoint();
|
|
|
|
main_switch_merge_ =
|
|
|
|
new spv::Block(builder_->getUniqueId(), *function_main_);
|
2020-11-03 20:31:52 +00:00
|
|
|
SpirvCreateSelectionMerge(main_switch_merge_->getId(),
|
|
|
|
spv::SelectionControlDontFlattenMask);
|
2020-10-18 12:36:02 +00:00
|
|
|
main_switch_op_ = std::make_unique<spv::Instruction>(spv::OpSwitch);
|
|
|
|
main_switch_op_->addIdOperand(main_loop_pc_current);
|
|
|
|
main_switch_op_->addIdOperand(main_switch_merge_->getId());
|
|
|
|
// The default case (the merge here) must have the header as a predecessor.
|
|
|
|
main_switch_merge_->addPredecessor(main_switch_header_);
|
|
|
|
// The instruction will be inserted later, when all cases are filled.
|
|
|
|
// Insert and enter case 0.
|
|
|
|
spv::Block* main_switch_case_0_block =
|
|
|
|
new spv::Block(builder_->getUniqueId(), *function_main_);
|
|
|
|
main_switch_op_->addImmediateOperand(0);
|
|
|
|
main_switch_op_->addIdOperand(main_switch_case_0_block->getId());
|
|
|
|
// Every switch case must have the OpSelectionMerge/OpSwitch block as a
|
|
|
|
// predecessor.
|
|
|
|
main_switch_case_0_block->addPredecessor(main_switch_header_);
|
|
|
|
function_main_->addBlock(main_switch_case_0_block);
|
|
|
|
builder_->setBuildPoint(main_switch_case_0_block);
|
|
|
|
}
|
2020-10-11 17:22:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
2020-10-25 12:09:39 +00:00
|
|
|
// Close flow control within the last switch case.
|
|
|
|
CloseExecConditionals();
|
2020-10-18 12:36:02 +00:00
|
|
|
bool has_main_switch = !label_addresses().empty();
|
|
|
|
// After the final exec (if it happened to be not exece, which would already
|
|
|
|
// have a break branch), break from the switch if it exists, or from the
|
|
|
|
// loop it doesn't.
|
|
|
|
if (!builder_->getBuildPoint()->isTerminated()) {
|
|
|
|
builder_->createBranch(has_main_switch ? main_switch_merge_
|
|
|
|
: main_loop_merge_);
|
|
|
|
}
|
|
|
|
if (has_main_switch) {
|
|
|
|
// Insert the switch instruction with all cases added as operands.
|
|
|
|
builder_->setBuildPoint(main_switch_header_);
|
|
|
|
builder_->getBuildPoint()->addInstruction(std::move(main_switch_op_));
|
|
|
|
// Build the main switch merge, breaking out of the loop after falling
|
|
|
|
// through the end or breaking from exece (only continuing if a jump - from
|
|
|
|
// a guest loop or from jmp/call - was made).
|
|
|
|
function_main_->addBlock(main_switch_merge_);
|
|
|
|
builder_->setBuildPoint(main_switch_merge_);
|
|
|
|
builder_->createBranch(main_loop_merge_);
|
|
|
|
}
|
|
|
|
|
2020-10-16 16:55:41 +00:00
|
|
|
// Main loop continuation - choose the program counter based on the path
|
|
|
|
// taken (-1 if not from a jump as a safe fallback, which would result in not
|
|
|
|
// hitting any switch case and reaching the final break in the body).
|
|
|
|
function_main_->addBlock(main_loop_continue_);
|
|
|
|
builder_->setBuildPoint(main_loop_continue_);
|
2020-10-18 12:36:02 +00:00
|
|
|
if (has_main_switch) {
|
2020-10-25 17:24:48 +00:00
|
|
|
// OpPhi, if added, must be the first in the block.
|
2020-10-18 12:36:02 +00:00
|
|
|
// If labels were added, but not jumps (for example, due to the call
|
|
|
|
// instruction not being implemented as of October 18, 2020), send an
|
|
|
|
// impossible program counter value (-1) to the OpPhi at the next iteration.
|
|
|
|
if (main_switch_next_pc_phi_operands_.empty()) {
|
|
|
|
main_switch_next_pc_phi_operands_.push_back(
|
|
|
|
builder_->makeIntConstant(-1));
|
|
|
|
}
|
2020-10-16 16:55:41 +00:00
|
|
|
std::unique_ptr<spv::Instruction> main_loop_pc_next_op =
|
2020-10-18 12:36:02 +00:00
|
|
|
std::make_unique<spv::Instruction>(
|
|
|
|
main_loop_pc_next_, type_int_,
|
|
|
|
main_switch_next_pc_phi_operands_.size() >= 2 ? spv::OpPhi
|
|
|
|
: spv::OpCopyObject);
|
|
|
|
for (spv::Id operand : main_switch_next_pc_phi_operands_) {
|
|
|
|
main_loop_pc_next_op->addIdOperand(operand);
|
|
|
|
}
|
2020-10-16 16:55:41 +00:00
|
|
|
builder_->getBuildPoint()->addInstruction(std::move(main_loop_pc_next_op));
|
|
|
|
}
|
|
|
|
builder_->createBranch(main_loop_header_);
|
2020-10-18 12:36:02 +00:00
|
|
|
|
2020-10-16 16:55:41 +00:00
|
|
|
// Add the main loop merge block and go back to the function.
|
|
|
|
function_main_->addBlock(main_loop_merge_);
|
|
|
|
builder_->setBuildPoint(main_loop_merge_);
|
|
|
|
|
2020-10-11 17:22:15 +00:00
|
|
|
if (IsSpirvVertexOrTessEvalShader()) {
|
|
|
|
CompleteVertexOrTessEvalShaderInMain();
|
|
|
|
}
|
|
|
|
|
2020-10-16 16:55:41 +00:00
|
|
|
// End the main function.
|
2020-10-11 17:22:15 +00:00
|
|
|
builder_->leaveFunction();
|
|
|
|
|
2020-10-16 16:55:41 +00:00
|
|
|
// Make the main function the entry point.
|
|
|
|
spv::ExecutionModel execution_model;
|
|
|
|
if (IsSpirvFragmentShader()) {
|
|
|
|
execution_model = spv::ExecutionModelFragment;
|
|
|
|
builder_->addExecutionMode(function_main_,
|
|
|
|
spv::ExecutionModeOriginUpperLeft);
|
|
|
|
} else {
|
|
|
|
assert_true(IsSpirvVertexOrTessEvalShader());
|
|
|
|
execution_model = IsSpirvTessEvalShader()
|
|
|
|
? spv::ExecutionModelTessellationEvaluation
|
|
|
|
: spv::ExecutionModelVertex;
|
|
|
|
}
|
2020-10-31 13:22:15 +00:00
|
|
|
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);
|
|
|
|
}
|
2020-10-16 16:55:41 +00:00
|
|
|
spv::Instruction* entry_point =
|
|
|
|
builder_->addEntryPoint(execution_model, function_main_, "main");
|
2020-10-31 13:22:15 +00:00
|
|
|
for (spv::Id interface_id : main_interface_) {
|
|
|
|
entry_point->addIdOperand(interface_id);
|
2020-10-16 16:55:41 +00:00
|
|
|
}
|
|
|
|
|
2020-10-11 17:22:15 +00:00
|
|
|
// TODO(Triang3l): Avoid copy?
|
|
|
|
std::vector<unsigned int> module_uints;
|
|
|
|
builder_->dump(module_uints);
|
|
|
|
std::vector<uint8_t> module_bytes;
|
|
|
|
module_bytes.reserve(sizeof(unsigned int) * module_uints.size());
|
|
|
|
module_bytes.insert(module_bytes.cend(),
|
|
|
|
reinterpret_cast<const uint8_t*>(module_uints.data()),
|
|
|
|
reinterpret_cast<const uint8_t*>(module_uints.data()) +
|
|
|
|
sizeof(unsigned int) * module_uints.size());
|
|
|
|
return module_bytes;
|
|
|
|
}
|
|
|
|
|
2020-10-18 12:36:02 +00:00
|
|
|
void SpirvShaderTranslator::ProcessLabel(uint32_t cf_index) {
|
|
|
|
if (cf_index == 0) {
|
|
|
|
// 0 already added in the beginning.
|
|
|
|
return;
|
|
|
|
}
|
2020-10-25 12:09:39 +00:00
|
|
|
|
|
|
|
assert_false(label_addresses().empty());
|
|
|
|
|
|
|
|
// Close flow control within the previous switch case.
|
|
|
|
CloseExecConditionals();
|
|
|
|
|
2020-10-18 12:36:02 +00:00
|
|
|
spv::Function& function = builder_->getBuildPoint()->getParent();
|
|
|
|
// Create the next switch case and fallthrough to it.
|
|
|
|
spv::Block* new_case = new spv::Block(builder_->getUniqueId(), function);
|
|
|
|
main_switch_op_->addImmediateOperand(cf_index);
|
|
|
|
main_switch_op_->addIdOperand(new_case->getId());
|
|
|
|
// Every switch case must have the OpSelectionMerge/OpSwitch block as a
|
|
|
|
// predecessor.
|
|
|
|
new_case->addPredecessor(main_switch_header_);
|
|
|
|
// The previous block may have already been terminated if was exece.
|
|
|
|
if (!builder_->getBuildPoint()->isTerminated()) {
|
|
|
|
builder_->createBranch(new_case);
|
|
|
|
}
|
|
|
|
function.addBlock(new_case);
|
|
|
|
builder_->setBuildPoint(new_case);
|
|
|
|
}
|
|
|
|
|
2020-10-25 12:09:39 +00:00
|
|
|
void SpirvShaderTranslator::ProcessExecInstructionBegin(
|
|
|
|
const ParsedExecInstruction& instr) {
|
|
|
|
UpdateExecConditionals(instr.type, instr.bool_constant_index,
|
|
|
|
instr.condition);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpirvShaderTranslator::ProcessExecInstructionEnd(
|
|
|
|
const ParsedExecInstruction& instr) {
|
|
|
|
if (instr.is_end) {
|
|
|
|
// Break out of the main switch (if exists) and the main loop.
|
|
|
|
CloseInstructionPredication();
|
|
|
|
if (!builder_->getBuildPoint()->isTerminated()) {
|
|
|
|
builder_->createBranch(label_addresses().empty() ? main_loop_merge_
|
|
|
|
: main_switch_merge_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
UpdateExecConditionals(instr.type, instr.bool_constant_index,
|
|
|
|
instr.condition);
|
|
|
|
}
|
|
|
|
|
2020-10-25 17:24:48 +00:00
|
|
|
void SpirvShaderTranslator::ProcessLoopStartInstruction(
|
|
|
|
const ParsedLoopStartInstruction& instr) {
|
|
|
|
// loop il<idx>, L<idx> - loop with loop data il<idx>, end @ L<idx>
|
|
|
|
|
|
|
|
// Loop control is outside execs - actually close the last exec.
|
|
|
|
CloseExecConditionals();
|
|
|
|
|
|
|
|
EnsureBuildPointAvailable();
|
|
|
|
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(3);
|
|
|
|
// Loop constants (member 1).
|
|
|
|
id_vector_temp_.push_back(builder_->makeIntConstant(1));
|
|
|
|
// 4-component vector.
|
|
|
|
id_vector_temp_.push_back(
|
|
|
|
builder_->makeIntConstant(int(instr.loop_constant_index >> 2)));
|
|
|
|
// Scalar within the vector.
|
|
|
|
id_vector_temp_.push_back(
|
|
|
|
builder_->makeIntConstant(int(instr.loop_constant_index & 3)));
|
|
|
|
// Count (unsigned) in bits 0:7 of the loop constant (struct member 1),
|
|
|
|
// initial aL (unsigned) in 8:15.
|
|
|
|
spv::Id loop_constant =
|
|
|
|
builder_->createLoad(builder_->createAccessChain(
|
|
|
|
spv::StorageClassUniform,
|
|
|
|
uniform_bool_loop_constants_, id_vector_temp_),
|
|
|
|
spv::NoPrecision);
|
|
|
|
|
|
|
|
spv::Id const_int_8 = builder_->makeIntConstant(8);
|
|
|
|
|
|
|
|
// Push the count to the loop count stack - move XYZ to YZW and set X to the
|
|
|
|
// new iteration count (swizzling the way glslang does it for similar GLSL).
|
|
|
|
spv::Id loop_count_stack_old =
|
|
|
|
builder_->createLoad(var_main_loop_count_, spv::NoPrecision);
|
|
|
|
spv::Id loop_count_new =
|
|
|
|
builder_->createTriOp(spv::OpBitFieldUExtract, type_uint_, loop_constant,
|
|
|
|
const_int_0_, const_int_8);
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(4);
|
|
|
|
id_vector_temp_.push_back(loop_count_new);
|
|
|
|
for (unsigned int i = 0; i < 3; ++i) {
|
|
|
|
id_vector_temp_.push_back(
|
|
|
|
builder_->createCompositeExtract(loop_count_stack_old, type_uint_, i));
|
|
|
|
}
|
|
|
|
builder_->createStore(
|
|
|
|
builder_->createCompositeConstruct(type_uint4_, id_vector_temp_),
|
|
|
|
var_main_loop_count_);
|
|
|
|
|
|
|
|
// Push aL - keep the same value as in the previous loop if repeating, or the
|
|
|
|
// new one otherwise.
|
|
|
|
spv::Id address_relative_stack_old =
|
|
|
|
builder_->createLoad(var_main_address_relative_, spv::NoPrecision);
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(4);
|
|
|
|
if (instr.is_repeat) {
|
|
|
|
id_vector_temp_.emplace_back();
|
|
|
|
} else {
|
|
|
|
id_vector_temp_.push_back(builder_->createUnaryOp(
|
|
|
|
spv::OpBitcast, type_int_,
|
|
|
|
builder_->createTriOp(spv::OpBitFieldUExtract, type_uint_,
|
|
|
|
loop_constant, const_int_8, const_int_8)));
|
|
|
|
}
|
|
|
|
for (unsigned int i = 0; i < 3; ++i) {
|
|
|
|
id_vector_temp_.push_back(builder_->createCompositeExtract(
|
|
|
|
address_relative_stack_old, type_int_, i));
|
|
|
|
}
|
|
|
|
if (instr.is_repeat) {
|
|
|
|
id_vector_temp_[0] = id_vector_temp_[1];
|
|
|
|
}
|
|
|
|
builder_->createStore(
|
|
|
|
builder_->createCompositeConstruct(type_int4_, id_vector_temp_),
|
|
|
|
var_main_address_relative_);
|
|
|
|
|
|
|
|
// Break (jump to the skip label) if the loop counter is 0 (since the
|
|
|
|
// condition is checked in the end).
|
|
|
|
spv::Block& head_block = *builder_->getBuildPoint();
|
|
|
|
spv::Id loop_count_zero = builder_->createBinOp(
|
|
|
|
spv::OpIEqual, type_bool_, loop_count_new, const_uint_0_);
|
|
|
|
spv::Block& skip_block = builder_->makeNewBlock();
|
|
|
|
spv::Block& body_block = builder_->makeNewBlock();
|
2020-11-03 20:31:52 +00:00
|
|
|
SpirvCreateSelectionMerge(body_block.getId());
|
2020-10-25 17:24:48 +00:00
|
|
|
{
|
|
|
|
std::unique_ptr<spv::Instruction> branch_conditional_op =
|
|
|
|
std::make_unique<spv::Instruction>(spv::OpBranchConditional);
|
|
|
|
branch_conditional_op->addIdOperand(loop_count_zero);
|
|
|
|
branch_conditional_op->addIdOperand(skip_block.getId());
|
|
|
|
branch_conditional_op->addIdOperand(body_block.getId());
|
|
|
|
// More likely to enter than to skip.
|
|
|
|
branch_conditional_op->addImmediateOperand(1);
|
|
|
|
branch_conditional_op->addImmediateOperand(2);
|
|
|
|
head_block.addInstruction(std::move(branch_conditional_op));
|
|
|
|
}
|
|
|
|
skip_block.addPredecessor(&head_block);
|
|
|
|
body_block.addPredecessor(&head_block);
|
|
|
|
builder_->setBuildPoint(&skip_block);
|
|
|
|
main_switch_next_pc_phi_operands_.push_back(
|
|
|
|
builder_->makeIntConstant(int(instr.loop_skip_address)));
|
|
|
|
main_switch_next_pc_phi_operands_.push_back(
|
|
|
|
builder_->getBuildPoint()->getId());
|
|
|
|
builder_->createBranch(main_loop_continue_);
|
|
|
|
builder_->setBuildPoint(&body_block);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpirvShaderTranslator::ProcessLoopEndInstruction(
|
|
|
|
const ParsedLoopEndInstruction& instr) {
|
|
|
|
// endloop il<idx>, L<idx> - end loop w/ data il<idx>, head @ L<idx>
|
|
|
|
|
|
|
|
// Loop control is outside execs - actually close the last exec.
|
|
|
|
CloseExecConditionals();
|
|
|
|
|
|
|
|
EnsureBuildPointAvailable();
|
|
|
|
|
|
|
|
// Subtract 1 from the loop counter (will store later).
|
|
|
|
spv::Id loop_count_stack_old =
|
|
|
|
builder_->createLoad(var_main_loop_count_, spv::NoPrecision);
|
|
|
|
spv::Id loop_count = builder_->createBinOp(
|
|
|
|
spv::OpISub, type_uint_,
|
|
|
|
builder_->createCompositeExtract(loop_count_stack_old, type_uint_, 0),
|
|
|
|
builder_->makeUintConstant(1));
|
|
|
|
spv::Id address_relative_stack_old =
|
|
|
|
builder_->createLoad(var_main_address_relative_, spv::NoPrecision);
|
|
|
|
|
|
|
|
// Predicated break works like break if (loop_count == 0 || [!]p0).
|
|
|
|
// Three options, due to logical operations usage (so OpLogicalNot is not
|
|
|
|
// required):
|
|
|
|
// - Continue if (loop_count != 0).
|
|
|
|
// - Continue if (loop_count != 0 && p0), if breaking if !p0.
|
|
|
|
// - Break if (loop_count == 0 || p0), if breaking if p0.
|
|
|
|
bool break_is_true = instr.is_predicated_break && instr.predicate_condition;
|
|
|
|
spv::Id condition =
|
|
|
|
builder_->createBinOp(break_is_true ? spv::OpIEqual : spv::OpINotEqual,
|
|
|
|
type_bool_, loop_count, const_uint_0_);
|
|
|
|
if (instr.is_predicated_break) {
|
|
|
|
condition = builder_->createBinOp(
|
|
|
|
instr.predicate_condition ? spv::OpLogicalOr : spv::OpLogicalAnd,
|
|
|
|
type_bool_, condition,
|
|
|
|
builder_->createLoad(var_main_predicate_, spv::NoPrecision));
|
|
|
|
}
|
|
|
|
|
|
|
|
spv::Block& body_block = *builder_->getBuildPoint();
|
|
|
|
spv::Block& continue_block = builder_->makeNewBlock();
|
|
|
|
spv::Block& break_block = builder_->makeNewBlock();
|
2020-11-03 20:31:52 +00:00
|
|
|
SpirvCreateSelectionMerge(break_block.getId());
|
2020-10-25 17:24:48 +00:00
|
|
|
{
|
|
|
|
std::unique_ptr<spv::Instruction> branch_conditional_op =
|
|
|
|
std::make_unique<spv::Instruction>(spv::OpBranchConditional);
|
|
|
|
branch_conditional_op->addIdOperand(condition);
|
|
|
|
// More likely to continue than to break.
|
|
|
|
if (break_is_true) {
|
|
|
|
branch_conditional_op->addIdOperand(break_block.getId());
|
|
|
|
branch_conditional_op->addIdOperand(continue_block.getId());
|
|
|
|
branch_conditional_op->addImmediateOperand(1);
|
|
|
|
branch_conditional_op->addImmediateOperand(2);
|
|
|
|
} else {
|
|
|
|
branch_conditional_op->addIdOperand(continue_block.getId());
|
|
|
|
branch_conditional_op->addIdOperand(break_block.getId());
|
|
|
|
branch_conditional_op->addImmediateOperand(2);
|
|
|
|
branch_conditional_op->addImmediateOperand(1);
|
|
|
|
}
|
|
|
|
body_block.addInstruction(std::move(branch_conditional_op));
|
|
|
|
}
|
|
|
|
continue_block.addPredecessor(&body_block);
|
|
|
|
break_block.addPredecessor(&body_block);
|
|
|
|
|
|
|
|
// Continue case.
|
|
|
|
builder_->setBuildPoint(&continue_block);
|
|
|
|
// Store the loop count with 1 subtracted.
|
|
|
|
builder_->createStore(builder_->createCompositeInsert(
|
|
|
|
loop_count, loop_count_stack_old, type_uint4_, 0),
|
|
|
|
var_main_loop_count_);
|
|
|
|
// Extract the value to add to aL (signed, in bits 16:23 of the loop
|
|
|
|
// constant).
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(3);
|
|
|
|
// Loop constants (member 1).
|
|
|
|
id_vector_temp_.push_back(builder_->makeIntConstant(1));
|
|
|
|
// 4-component vector.
|
|
|
|
id_vector_temp_.push_back(
|
|
|
|
builder_->makeIntConstant(int(instr.loop_constant_index >> 2)));
|
|
|
|
// Scalar within the vector.
|
|
|
|
id_vector_temp_.push_back(
|
|
|
|
builder_->makeIntConstant(int(instr.loop_constant_index & 3)));
|
|
|
|
spv::Id loop_constant =
|
|
|
|
builder_->createLoad(builder_->createAccessChain(
|
|
|
|
spv::StorageClassUniform,
|
|
|
|
uniform_bool_loop_constants_, id_vector_temp_),
|
|
|
|
spv::NoPrecision);
|
|
|
|
spv::Id address_relative_old = builder_->createCompositeExtract(
|
|
|
|
address_relative_stack_old, type_int_, 0);
|
|
|
|
builder_->createStore(
|
|
|
|
builder_->createCompositeInsert(
|
|
|
|
builder_->createBinOp(
|
|
|
|
spv::OpIAdd, type_int_, address_relative_old,
|
|
|
|
builder_->createTriOp(
|
|
|
|
spv::OpBitFieldSExtract, type_int_,
|
|
|
|
builder_->createUnaryOp(spv::OpBitcast, type_int_,
|
|
|
|
loop_constant),
|
|
|
|
builder_->makeIntConstant(16), builder_->makeIntConstant(8))),
|
|
|
|
address_relative_stack_old, type_int4_, 0),
|
|
|
|
var_main_address_relative_);
|
|
|
|
// Jump back to the beginning of the loop body.
|
|
|
|
main_switch_next_pc_phi_operands_.push_back(
|
|
|
|
builder_->makeIntConstant(int(instr.loop_body_address)));
|
|
|
|
main_switch_next_pc_phi_operands_.push_back(
|
|
|
|
builder_->getBuildPoint()->getId());
|
|
|
|
builder_->createBranch(main_loop_continue_);
|
|
|
|
|
|
|
|
// Break case.
|
|
|
|
builder_->setBuildPoint(&break_block);
|
|
|
|
// Pop the current loop off the loop counter and the relative address stacks -
|
|
|
|
// move YZW to XYZ and set W to 0.
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(4);
|
|
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
|
|
id_vector_temp_.push_back(
|
|
|
|
builder_->createCompositeExtract(loop_count_stack_old, type_uint_, i));
|
|
|
|
}
|
|
|
|
id_vector_temp_.push_back(const_uint_0_);
|
|
|
|
builder_->createStore(
|
|
|
|
builder_->createCompositeConstruct(type_uint4_, id_vector_temp_),
|
|
|
|
var_main_loop_count_);
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(4);
|
|
|
|
for (unsigned int i = 1; i < 4; ++i) {
|
|
|
|
id_vector_temp_.push_back(builder_->createCompositeExtract(
|
|
|
|
address_relative_stack_old, type_int_, i));
|
|
|
|
}
|
|
|
|
id_vector_temp_.push_back(const_int_0_);
|
|
|
|
builder_->createStore(
|
|
|
|
builder_->createCompositeConstruct(type_int4_, id_vector_temp_),
|
|
|
|
var_main_address_relative_);
|
|
|
|
// Now going to fall through to the next control flow instruction.
|
|
|
|
}
|
|
|
|
|
2020-10-25 12:09:39 +00:00
|
|
|
void SpirvShaderTranslator::ProcessJumpInstruction(
|
|
|
|
const ParsedJumpInstruction& instr) {
|
|
|
|
// Treat like exec, merge with execs if possible, since it's an if too.
|
|
|
|
ParsedExecInstruction::Type type;
|
|
|
|
if (instr.type == ParsedJumpInstruction::Type::kConditional) {
|
|
|
|
type = ParsedExecInstruction::Type::kConditional;
|
|
|
|
} else if (instr.type == ParsedJumpInstruction::Type::kPredicated) {
|
|
|
|
type = ParsedExecInstruction::Type::kPredicated;
|
|
|
|
} else {
|
|
|
|
type = ParsedExecInstruction::Type::kUnconditional;
|
|
|
|
}
|
|
|
|
UpdateExecConditionals(type, instr.bool_constant_index, instr.condition);
|
|
|
|
|
|
|
|
// UpdateExecConditionals may not necessarily close the instruction-level
|
|
|
|
// predicate check (it's not necessary if the execs are merged), but here the
|
|
|
|
// instruction itself is on the control flow level, so the predicate check is
|
|
|
|
// on the control flow level too.
|
|
|
|
CloseInstructionPredication();
|
|
|
|
|
2020-10-25 17:24:48 +00:00
|
|
|
if (builder_->getBuildPoint()->isTerminated()) {
|
|
|
|
// Unreachable for some reason.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
main_switch_next_pc_phi_operands_.push_back(
|
|
|
|
builder_->makeIntConstant(int(instr.target_address)));
|
|
|
|
main_switch_next_pc_phi_operands_.push_back(
|
|
|
|
builder_->getBuildPoint()->getId());
|
|
|
|
builder_->createBranch(main_loop_continue_);
|
2020-10-25 12:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SpirvShaderTranslator::EnsureBuildPointAvailable() {
|
|
|
|
if (!builder_->getBuildPoint()->isTerminated()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
spv::Block& new_block = builder_->makeNewBlock();
|
|
|
|
new_block.setUnreachable();
|
|
|
|
builder_->setBuildPoint(&new_block);
|
|
|
|
}
|
|
|
|
|
2020-10-11 17:22:15 +00:00
|
|
|
void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
|
|
|
|
// Create the inputs.
|
|
|
|
if (IsSpirvTessEvalShader()) {
|
2020-10-16 16:55:41 +00:00
|
|
|
input_primitive_id_ = builder_->createVariable(
|
2020-10-11 17:22:15 +00:00
|
|
|
spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_PrimitiveID");
|
2020-10-16 16:55:41 +00:00
|
|
|
builder_->addDecoration(input_primitive_id_, spv::DecorationBuiltIn,
|
2020-10-11 17:22:15 +00:00
|
|
|
spv::BuiltInPrimitiveId);
|
2020-10-31 13:22:15 +00:00
|
|
|
main_interface_.push_back(input_primitive_id_);
|
2020-10-11 17:22:15 +00:00
|
|
|
} else {
|
2020-10-16 16:55:41 +00:00
|
|
|
input_vertex_index_ = builder_->createVariable(
|
2020-10-11 17:22:15 +00:00
|
|
|
spv::NoPrecision, spv::StorageClassInput, type_int_, "gl_VertexIndex");
|
2020-10-16 16:55:41 +00:00
|
|
|
builder_->addDecoration(input_vertex_index_, spv::DecorationBuiltIn,
|
2020-10-11 17:22:15 +00:00
|
|
|
spv::BuiltInVertexIndex);
|
2020-10-31 13:22:15 +00:00
|
|
|
main_interface_.push_back(input_vertex_index_);
|
2020-10-11 17:22:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create the entire GLSL 4.50 gl_PerVertex output similar to what glslang
|
|
|
|
// does. Members (like gl_PointSize) don't need to be used, and also
|
|
|
|
// 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.
|
2020-10-31 13:22:15 +00:00
|
|
|
if (features_.clip_distance) {
|
2020-10-11 17:22:15 +00:00
|
|
|
builder_->addCapability(spv::CapabilityClipDistance);
|
|
|
|
}
|
2020-10-31 13:22:15 +00:00
|
|
|
if (features_.cull_distance) {
|
2020-10-11 17:22:15 +00:00
|
|
|
builder_->addCapability(spv::CapabilityCullDistance);
|
|
|
|
}
|
|
|
|
std::vector<spv::Id> struct_per_vertex_members;
|
|
|
|
struct_per_vertex_members.reserve(kOutputPerVertexMemberCount);
|
|
|
|
struct_per_vertex_members.push_back(type_float4_);
|
|
|
|
struct_per_vertex_members.push_back(type_float_);
|
|
|
|
// 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(
|
2020-10-31 13:22:15 +00:00
|
|
|
type_float_, builder_->makeUintConstant(features_.clip_distance ? 6 : 1),
|
2020-10-11 17:22:15 +00:00
|
|
|
0));
|
|
|
|
struct_per_vertex_members.push_back(
|
|
|
|
builder_->makeArrayType(type_float_, builder_->makeUintConstant(1), 0));
|
|
|
|
spv::Id type_struct_per_vertex =
|
|
|
|
builder_->makeStructType(struct_per_vertex_members, "gl_PerVertex");
|
2020-10-27 19:48:47 +00:00
|
|
|
builder_->addMemberDecoration(type_struct_per_vertex,
|
|
|
|
kOutputPerVertexMemberPosition,
|
|
|
|
spv::DecorationInvariant);
|
2020-10-11 17:22:15 +00:00
|
|
|
builder_->addMemberDecoration(type_struct_per_vertex,
|
|
|
|
kOutputPerVertexMemberPosition,
|
|
|
|
spv::DecorationBuiltIn, spv::BuiltInPosition);
|
|
|
|
builder_->addMemberDecoration(type_struct_per_vertex,
|
|
|
|
kOutputPerVertexMemberPointSize,
|
|
|
|
spv::DecorationBuiltIn, spv::BuiltInPointSize);
|
2020-10-27 19:48:47 +00:00
|
|
|
builder_->addMemberDecoration(type_struct_per_vertex,
|
|
|
|
kOutputPerVertexMemberClipDistance,
|
|
|
|
spv::DecorationInvariant);
|
2020-10-11 17:22:15 +00:00
|
|
|
builder_->addMemberDecoration(
|
|
|
|
type_struct_per_vertex, kOutputPerVertexMemberClipDistance,
|
|
|
|
spv::DecorationBuiltIn, spv::BuiltInClipDistance);
|
2020-10-27 19:48:47 +00:00
|
|
|
builder_->addMemberDecoration(type_struct_per_vertex,
|
|
|
|
kOutputPerVertexMemberCullDistance,
|
|
|
|
spv::DecorationInvariant);
|
2020-10-11 17:22:15 +00:00
|
|
|
builder_->addMemberDecoration(
|
|
|
|
type_struct_per_vertex, kOutputPerVertexMemberCullDistance,
|
|
|
|
spv::DecorationBuiltIn, spv::BuiltInCullDistance);
|
|
|
|
builder_->addDecoration(type_struct_per_vertex, spv::DecorationBlock);
|
|
|
|
output_per_vertex_ =
|
|
|
|
builder_->createVariable(spv::NoPrecision, spv::StorageClassOutput,
|
|
|
|
type_struct_per_vertex, "xe_out_gl_PerVertex");
|
2020-10-31 13:22:15 +00:00
|
|
|
main_interface_.push_back(output_per_vertex_);
|
2020-10-11 17:22:15 +00:00
|
|
|
}
|
|
|
|
|
2020-10-16 16:55:41 +00:00
|
|
|
void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() {
|
|
|
|
var_main_point_size_edge_flag_kill_vertex_ = builder_->createVariable(
|
|
|
|
spv::NoPrecision, spv::StorageClassFunction, type_float3_,
|
|
|
|
"xe_var_point_size_edge_flag_kill_vertex");
|
2020-11-03 20:31:52 +00:00
|
|
|
|
|
|
|
// Load the vertex index or the tessellation parameters.
|
|
|
|
if (register_count()) {
|
|
|
|
// TODO(Triang3l): Barycentric coordinates and patch index.
|
|
|
|
if (IsSpirvVertexShader()) {
|
|
|
|
// TODO(Triang3l): Fetch the vertex index from the shared memory when
|
|
|
|
// fullDrawIndexUint32 isn't available and the index is 32-bit and needs
|
|
|
|
// endian swap.
|
|
|
|
// TODO(Triang3l): Close line loop primitive.
|
|
|
|
// Load the unswapped index as uint for swapping.
|
|
|
|
spv::Id vertex_index = builder_->createUnaryOp(
|
|
|
|
spv::OpBitcast, type_uint_,
|
|
|
|
builder_->createLoad(input_vertex_index_, spv::NoPrecision));
|
|
|
|
// Endian-swap the index and convert to int.
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.push_back(
|
|
|
|
builder_->makeIntConstant(kSystemConstantIndexVertexIndexEndian));
|
|
|
|
spv::Id vertex_index_endian =
|
|
|
|
builder_->createLoad(builder_->createAccessChain(
|
|
|
|
spv::StorageClassUniform,
|
|
|
|
uniform_system_constants_, id_vector_temp_),
|
|
|
|
spv::NoPrecision);
|
|
|
|
vertex_index = builder_->createUnaryOp(
|
|
|
|
spv::OpBitcast, type_int_,
|
|
|
|
EndianSwap32Uint(vertex_index, vertex_index_endian));
|
|
|
|
// Add the base to the index.
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.push_back(
|
|
|
|
builder_->makeIntConstant(kSystemConstantIndexVertexBaseIndex));
|
|
|
|
vertex_index = builder_->createBinOp(
|
|
|
|
spv::OpIAdd, type_int_, vertex_index,
|
|
|
|
builder_->createLoad(builder_->createAccessChain(
|
|
|
|
spv::StorageClassUniform,
|
|
|
|
uniform_system_constants_, id_vector_temp_),
|
|
|
|
spv::NoPrecision));
|
|
|
|
// Write the index to r0.x as float.
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(2);
|
|
|
|
id_vector_temp_.push_back(const_int_0_);
|
|
|
|
id_vector_temp_.push_back(const_int_0_);
|
|
|
|
builder_->createStore(
|
|
|
|
builder_->createUnaryOp(spv::OpConvertSToF, type_float_,
|
|
|
|
vertex_index),
|
|
|
|
builder_->createAccessChain(spv::StorageClassFunction,
|
|
|
|
var_main_registers_, id_vector_temp_));
|
|
|
|
}
|
|
|
|
}
|
2020-10-16 16:55:41 +00:00
|
|
|
}
|
|
|
|
|
2020-10-11 17:22:15 +00:00
|
|
|
void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {}
|
|
|
|
|
2020-10-25 12:09:39 +00:00
|
|
|
void SpirvShaderTranslator::UpdateExecConditionals(
|
|
|
|
ParsedExecInstruction::Type type, uint32_t bool_constant_index,
|
|
|
|
bool condition) {
|
|
|
|
// Check if we can merge the new exec with the previous one, or the jump with
|
|
|
|
// the previous exec. The instruction-level predicate check is also merged in
|
|
|
|
// this case.
|
|
|
|
if (type == ParsedExecInstruction::Type::kConditional) {
|
|
|
|
// Can merge conditional with conditional, as long as the bool constant and
|
|
|
|
// the expected values are the same.
|
|
|
|
if (cf_exec_conditional_merge_ &&
|
|
|
|
cf_exec_bool_constant_or_predicate_ == bool_constant_index &&
|
|
|
|
cf_exec_condition_ == condition) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (type == ParsedExecInstruction::Type::kPredicated) {
|
|
|
|
// Can merge predicated with predicated if the conditions are the same and
|
|
|
|
// the previous exec hasn't modified the predicate register.
|
|
|
|
if (!cf_exec_predicate_written_ && cf_exec_conditional_merge_ &&
|
|
|
|
cf_exec_bool_constant_or_predicate_ == kCfExecBoolConstantPredicate &&
|
|
|
|
cf_exec_condition_ == condition) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Can merge unconditional with unconditional.
|
|
|
|
assert_true(type == ParsedExecInstruction::Type::kUnconditional);
|
|
|
|
if (!cf_exec_conditional_merge_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CloseExecConditionals();
|
|
|
|
|
|
|
|
if (type == ParsedExecInstruction::Type::kUnconditional) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
EnsureBuildPointAvailable();
|
|
|
|
spv::Id condition_id;
|
|
|
|
if (type == ParsedExecInstruction::Type::kConditional) {
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(3);
|
|
|
|
// Bool constants (member 0).
|
|
|
|
id_vector_temp_.push_back(const_int_0_);
|
|
|
|
// 128-bit vector.
|
|
|
|
id_vector_temp_.push_back(
|
|
|
|
builder_->makeIntConstant(int(bool_constant_index >> 7)));
|
|
|
|
// 32-bit scalar of a 128-bit vector.
|
|
|
|
id_vector_temp_.push_back(
|
2020-10-25 17:24:48 +00:00
|
|
|
builder_->makeIntConstant(int((bool_constant_index >> 5) & 3)));
|
2020-10-25 12:09:39 +00:00
|
|
|
spv::Id bool_constant_scalar =
|
|
|
|
builder_->createLoad(builder_->createAccessChain(
|
|
|
|
spv::StorageClassUniform,
|
|
|
|
uniform_bool_loop_constants_, id_vector_temp_),
|
|
|
|
spv::NoPrecision);
|
|
|
|
condition_id = builder_->createBinOp(
|
|
|
|
spv::OpINotEqual, type_bool_,
|
|
|
|
builder_->createBinOp(
|
|
|
|
spv::OpBitwiseAnd, type_uint_, bool_constant_scalar,
|
|
|
|
builder_->makeUintConstant(uint32_t(1)
|
|
|
|
<< (bool_constant_index & 31))),
|
|
|
|
const_uint_0_);
|
|
|
|
cf_exec_bool_constant_or_predicate_ = bool_constant_index;
|
|
|
|
} else if (type == ParsedExecInstruction::Type::kPredicated) {
|
|
|
|
condition_id = builder_->createLoad(var_main_predicate_, spv::NoPrecision);
|
|
|
|
cf_exec_bool_constant_or_predicate_ = kCfExecBoolConstantPredicate;
|
|
|
|
} else {
|
|
|
|
assert_unhandled_case(type);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
cf_exec_condition_ = condition;
|
|
|
|
spv::Function& function = builder_->getBuildPoint()->getParent();
|
|
|
|
cf_exec_conditional_merge_ =
|
|
|
|
new spv::Block(builder_->getUniqueId(), function);
|
2020-11-03 20:31:52 +00:00
|
|
|
SpirvCreateSelectionMerge(cf_exec_conditional_merge_->getId());
|
2020-10-25 12:09:39 +00:00
|
|
|
spv::Block& inner_block = builder_->makeNewBlock();
|
|
|
|
builder_->createConditionalBranch(
|
|
|
|
condition_id, condition ? &inner_block : cf_exec_conditional_merge_,
|
|
|
|
condition ? cf_exec_conditional_merge_ : &inner_block);
|
|
|
|
builder_->setBuildPoint(&inner_block);
|
|
|
|
}
|
|
|
|
|
2020-10-26 19:12:01 +00:00
|
|
|
void SpirvShaderTranslator::UpdateInstructionPredication(bool predicated,
|
|
|
|
bool condition) {
|
|
|
|
if (!predicated) {
|
|
|
|
CloseInstructionPredication();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cf_instruction_predicate_merge_) {
|
|
|
|
if (cf_instruction_predicate_condition_ == condition) {
|
|
|
|
// Already in the needed instruction-level conditional.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
CloseInstructionPredication();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the instruction predicate condition is the same as the exec predicate
|
|
|
|
// condition, no need to open a check. However, if there was a `setp` prior
|
|
|
|
// to this instruction, the predicate value now may be different than it was
|
|
|
|
// in the beginning of the exec.
|
|
|
|
if (!cf_exec_predicate_written_ && cf_exec_conditional_merge_ &&
|
|
|
|
cf_exec_bool_constant_or_predicate_ == kCfExecBoolConstantPredicate &&
|
|
|
|
cf_exec_condition_ == condition) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
cf_instruction_predicate_condition_ = condition;
|
|
|
|
EnsureBuildPointAvailable();
|
|
|
|
spv::Id predicate_id =
|
|
|
|
builder_->createLoad(var_main_predicate_, spv::NoPrecision);
|
|
|
|
spv::Block& predicated_block = builder_->makeNewBlock();
|
|
|
|
cf_instruction_predicate_merge_ = &builder_->makeNewBlock();
|
2020-11-03 20:31:52 +00:00
|
|
|
SpirvCreateSelectionMerge(cf_instruction_predicate_merge_->getId());
|
2020-10-26 19:12:01 +00:00
|
|
|
builder_->createConditionalBranch(
|
|
|
|
predicate_id,
|
|
|
|
condition ? &predicated_block : cf_instruction_predicate_merge_,
|
|
|
|
condition ? cf_instruction_predicate_merge_ : &predicated_block);
|
|
|
|
builder_->setBuildPoint(&predicated_block);
|
|
|
|
}
|
|
|
|
|
2020-10-25 12:09:39 +00:00
|
|
|
void SpirvShaderTranslator::CloseInstructionPredication() {
|
|
|
|
if (!cf_instruction_predicate_merge_) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
spv::Block& inner_block = *builder_->getBuildPoint();
|
|
|
|
if (!inner_block.isTerminated()) {
|
|
|
|
builder_->createBranch(cf_instruction_predicate_merge_);
|
|
|
|
}
|
|
|
|
inner_block.getParent().addBlock(cf_instruction_predicate_merge_);
|
|
|
|
builder_->setBuildPoint(cf_instruction_predicate_merge_);
|
|
|
|
cf_instruction_predicate_merge_ = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void SpirvShaderTranslator::CloseExecConditionals() {
|
|
|
|
// Within the exec - instruction-level predicate check.
|
|
|
|
CloseInstructionPredication();
|
|
|
|
// Exec level.
|
|
|
|
if (cf_exec_conditional_merge_) {
|
|
|
|
spv::Block& inner_block = *builder_->getBuildPoint();
|
|
|
|
if (!inner_block.isTerminated()) {
|
|
|
|
builder_->createBranch(cf_exec_conditional_merge_);
|
|
|
|
}
|
|
|
|
inner_block.getParent().addBlock(cf_exec_conditional_merge_);
|
|
|
|
builder_->setBuildPoint(cf_exec_conditional_merge_);
|
|
|
|
cf_exec_conditional_merge_ = nullptr;
|
|
|
|
}
|
|
|
|
// Nothing relies on the predicate value being unchanged now.
|
|
|
|
cf_exec_predicate_written_ = false;
|
|
|
|
}
|
|
|
|
|
2020-10-27 19:48:47 +00:00
|
|
|
spv::Id SpirvShaderTranslator::GetStorageAddressingIndex(
|
|
|
|
InstructionStorageAddressingMode addressing_mode, uint32_t storage_index) {
|
|
|
|
EnsureBuildPointAvailable();
|
|
|
|
spv::Id base_pointer = spv::NoResult;
|
|
|
|
switch (addressing_mode) {
|
|
|
|
case InstructionStorageAddressingMode::kStatic:
|
|
|
|
return builder_->makeIntConstant(int(storage_index));
|
|
|
|
case InstructionStorageAddressingMode::kAddressAbsolute:
|
|
|
|
base_pointer = var_main_address_absolute_;
|
|
|
|
break;
|
|
|
|
case InstructionStorageAddressingMode::kAddressRelative:
|
|
|
|
// Load X component.
|
|
|
|
id_vector_temp_util_.clear();
|
|
|
|
id_vector_temp_util_.push_back(const_int_0_);
|
|
|
|
base_pointer = builder_->createAccessChain(spv::StorageClassFunction,
|
|
|
|
var_main_address_relative_,
|
|
|
|
id_vector_temp_util_);
|
|
|
|
break;
|
|
|
|
}
|
2020-10-29 19:07:02 +00:00
|
|
|
assert_true(base_pointer != spv::NoResult);
|
2020-10-27 19:48:47 +00:00
|
|
|
spv::Id index = builder_->createLoad(base_pointer, spv::NoPrecision);
|
|
|
|
if (storage_index) {
|
|
|
|
index =
|
|
|
|
builder_->createBinOp(spv::OpIAdd, type_int_, index,
|
|
|
|
builder_->makeIntConstant(int(storage_index)));
|
|
|
|
}
|
|
|
|
return index;
|
|
|
|
}
|
|
|
|
|
|
|
|
spv::Id SpirvShaderTranslator::LoadOperandStorage(
|
|
|
|
const InstructionOperand& operand) {
|
|
|
|
spv::Id index = GetStorageAddressingIndex(operand.storage_addressing_mode,
|
|
|
|
operand.storage_index);
|
|
|
|
EnsureBuildPointAvailable();
|
|
|
|
spv::Id vec4_pointer = spv::NoResult;
|
|
|
|
switch (operand.storage_source) {
|
|
|
|
case InstructionStorageSource::kRegister:
|
2020-10-29 19:07:02 +00:00
|
|
|
assert_true(var_main_registers_ != spv::NoResult);
|
2020-10-27 19:48:47 +00:00
|
|
|
id_vector_temp_util_.clear();
|
|
|
|
// Array element.
|
|
|
|
id_vector_temp_util_.push_back(index);
|
|
|
|
vec4_pointer = builder_->createAccessChain(
|
|
|
|
spv::StorageClassFunction, var_main_registers_, id_vector_temp_util_);
|
|
|
|
break;
|
|
|
|
case InstructionStorageSource::kConstantFloat:
|
2020-10-29 19:07:02 +00:00
|
|
|
assert_true(uniform_float_constants_ != spv::NoResult);
|
2020-10-27 19:48:47 +00:00
|
|
|
id_vector_temp_util_.clear();
|
|
|
|
id_vector_temp_util_.reserve(2);
|
|
|
|
// The first and the only structure member.
|
|
|
|
id_vector_temp_util_.push_back(const_int_0_);
|
|
|
|
// Array element.
|
|
|
|
id_vector_temp_util_.push_back(index);
|
|
|
|
vec4_pointer = builder_->createAccessChain(spv::StorageClassUniform,
|
|
|
|
uniform_float_constants_,
|
|
|
|
id_vector_temp_util_);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert_unhandled_case(operand.storage_source);
|
|
|
|
}
|
2020-10-29 19:07:02 +00:00
|
|
|
assert_true(vec4_pointer != spv::NoResult);
|
2020-10-27 19:48:47 +00:00
|
|
|
return builder_->createLoad(vec4_pointer, spv::NoPrecision);
|
|
|
|
}
|
|
|
|
|
|
|
|
spv::Id SpirvShaderTranslator::ApplyOperandModifiers(
|
|
|
|
spv::Id operand_value, const InstructionOperand& original_operand,
|
|
|
|
bool invert_negate, bool force_absolute) {
|
|
|
|
spv::Id type = builder_->getTypeId(operand_value);
|
|
|
|
assert_true(type != spv::NoType);
|
|
|
|
if (type == spv::NoType) {
|
|
|
|
return operand_value;
|
|
|
|
}
|
|
|
|
if (original_operand.is_absolute_value || force_absolute) {
|
|
|
|
EnsureBuildPointAvailable();
|
|
|
|
id_vector_temp_util_.clear();
|
|
|
|
id_vector_temp_util_.push_back(operand_value);
|
|
|
|
operand_value = builder_->createBuiltinCall(
|
|
|
|
type, ext_inst_glsl_std_450_, GLSLstd450FAbs, id_vector_temp_util_);
|
|
|
|
}
|
|
|
|
if (original_operand.is_negated != invert_negate) {
|
|
|
|
EnsureBuildPointAvailable();
|
|
|
|
operand_value =
|
|
|
|
builder_->createUnaryOp(spv::OpFNegate, type, operand_value);
|
|
|
|
builder_->addDecoration(operand_value, spv::DecorationNoContraction);
|
|
|
|
}
|
|
|
|
return operand_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
spv::Id SpirvShaderTranslator::GetUnmodifiedOperandComponents(
|
|
|
|
spv::Id operand_storage, const InstructionOperand& original_operand,
|
|
|
|
uint32_t components) {
|
|
|
|
assert_not_zero(components);
|
|
|
|
if (!components) {
|
|
|
|
return spv::NoResult;
|
|
|
|
}
|
|
|
|
assert_true(components <= 0b1111);
|
|
|
|
if (components == 0b1111 && original_operand.IsStandardSwizzle()) {
|
|
|
|
return operand_storage;
|
|
|
|
}
|
|
|
|
EnsureBuildPointAvailable();
|
|
|
|
uint32_t component_count = xe::bit_count(components);
|
|
|
|
if (component_count == 1) {
|
|
|
|
uint32_t scalar_index;
|
|
|
|
xe::bit_scan_forward(components, &scalar_index);
|
|
|
|
return builder_->createCompositeExtract(
|
|
|
|
operand_storage, type_float_,
|
|
|
|
static_cast<unsigned int>(original_operand.GetComponent(scalar_index)) -
|
|
|
|
static_cast<unsigned int>(SwizzleSource::kX));
|
|
|
|
}
|
|
|
|
id_vector_temp_util_.clear();
|
|
|
|
id_vector_temp_util_.reserve(component_count);
|
|
|
|
uint32_t components_remaining = components;
|
|
|
|
uint32_t component_index;
|
|
|
|
while (xe::bit_scan_forward(components_remaining, &component_index)) {
|
|
|
|
components_remaining &= ~(uint32_t(1) << component_index);
|
|
|
|
id_vector_temp_util_.push_back(
|
|
|
|
static_cast<unsigned int>(
|
|
|
|
original_operand.GetComponent(component_index)) -
|
|
|
|
static_cast<unsigned int>(SwizzleSource::kX));
|
|
|
|
}
|
|
|
|
return builder_->createRvalueSwizzle(spv::NoPrecision,
|
|
|
|
type_float_vectors_[component_count - 1],
|
|
|
|
operand_storage, id_vector_temp_util_);
|
|
|
|
}
|
|
|
|
|
2020-11-01 17:42:59 +00:00
|
|
|
void SpirvShaderTranslator::GetOperandScalarXY(
|
|
|
|
spv::Id operand_storage, const InstructionOperand& original_operand,
|
|
|
|
spv::Id& a_out, spv::Id& b_out, bool invert_negate, bool force_absolute) {
|
|
|
|
spv::Id a = GetOperandComponents(operand_storage, original_operand, 0b0001,
|
|
|
|
invert_negate, force_absolute);
|
|
|
|
a_out = a;
|
|
|
|
b_out = original_operand.GetComponent(0) != original_operand.GetComponent(1)
|
|
|
|
? GetOperandComponents(operand_storage, original_operand, 0b0010,
|
|
|
|
invert_negate, force_absolute)
|
|
|
|
: a;
|
|
|
|
}
|
|
|
|
|
|
|
|
spv::Id SpirvShaderTranslator::GetAbsoluteOperand(
|
|
|
|
spv::Id operand_storage, const InstructionOperand& original_operand) {
|
|
|
|
if (original_operand.is_absolute_value && !original_operand.is_negated) {
|
|
|
|
return operand_storage;
|
|
|
|
}
|
|
|
|
EnsureBuildPointAvailable();
|
|
|
|
id_vector_temp_util_.clear();
|
|
|
|
id_vector_temp_util_.push_back(operand_storage);
|
|
|
|
return builder_->createBuiltinCall(builder_->getTypeId(operand_storage),
|
|
|
|
ext_inst_glsl_std_450_, GLSLstd450FAbs,
|
|
|
|
id_vector_temp_util_);
|
|
|
|
}
|
|
|
|
|
2020-10-29 19:07:02 +00:00
|
|
|
void SpirvShaderTranslator::StoreResult(const InstructionResult& result,
|
|
|
|
spv::Id value) {
|
|
|
|
uint32_t used_write_mask = result.GetUsedWriteMask();
|
|
|
|
if (!used_write_mask) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
EnsureBuildPointAvailable();
|
|
|
|
|
|
|
|
spv::Id target_pointer = spv::NoResult;
|
|
|
|
switch (result.storage_target) {
|
|
|
|
case InstructionStorageTarget::kNone:
|
|
|
|
break;
|
|
|
|
case InstructionStorageTarget::kRegister: {
|
|
|
|
assert_true(var_main_registers_ != spv::NoResult);
|
|
|
|
// Must call GetStorageAddressingIndex first because of
|
|
|
|
// id_vector_temp_util_ usage in it.
|
|
|
|
spv::Id register_index = GetStorageAddressingIndex(
|
|
|
|
result.storage_addressing_mode, result.storage_index);
|
|
|
|
id_vector_temp_util_.clear();
|
|
|
|
// Array element.
|
|
|
|
id_vector_temp_util_.push_back(register_index);
|
|
|
|
target_pointer = builder_->createAccessChain(
|
|
|
|
spv::StorageClassFunction, var_main_registers_, id_vector_temp_util_);
|
|
|
|
} break;
|
|
|
|
case InstructionStorageTarget::kPosition:
|
|
|
|
assert_true(IsSpirvVertexOrTessEvalShader());
|
|
|
|
id_vector_temp_util_.clear();
|
|
|
|
id_vector_temp_util_.push_back(
|
|
|
|
builder_->makeIntConstant(kOutputPerVertexMemberPosition));
|
|
|
|
target_pointer = builder_->createAccessChain(
|
|
|
|
spv::StorageClassOutput, output_per_vertex_, id_vector_temp_util_);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// TODO(Triang3l): All storage targets.
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (target_pointer == spv::NoResult) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t constant_values;
|
|
|
|
uint32_t constant_components =
|
|
|
|
result.GetUsedConstantComponents(constant_values);
|
|
|
|
if (value == spv::NoResult) {
|
|
|
|
// The instruction processing function decided that nothing useful needs to
|
|
|
|
// be stored for some reason, however, some components still need to be
|
|
|
|
// written on the guest side - fill them with zeros.
|
|
|
|
constant_components = used_write_mask;
|
|
|
|
}
|
|
|
|
uint32_t non_constant_components = used_write_mask & ~constant_components;
|
|
|
|
|
|
|
|
unsigned int value_num_components =
|
|
|
|
value != spv::NoResult
|
|
|
|
? static_cast<unsigned int>(builder_->getNumComponents(value))
|
|
|
|
: 0;
|
|
|
|
|
|
|
|
if (result.is_clamped && non_constant_components) {
|
|
|
|
// Apply the saturation modifier to the result.
|
|
|
|
id_vector_temp_util_.clear();
|
|
|
|
id_vector_temp_util_.reserve(3);
|
|
|
|
id_vector_temp_util_.push_back(value);
|
|
|
|
id_vector_temp_util_.push_back(
|
|
|
|
const_float_vectors_0_[value_num_components - 1]);
|
|
|
|
id_vector_temp_util_.push_back(
|
|
|
|
const_float_vectors_1_[value_num_components - 1]);
|
|
|
|
value = builder_->createBuiltinCall(
|
|
|
|
type_float_vectors_[value_num_components - 1], ext_inst_glsl_std_450_,
|
|
|
|
GLSLstd450NClamp, id_vector_temp_util_);
|
|
|
|
}
|
|
|
|
|
|
|
|
// The value contains either result.GetUsedResultComponents() in a condensed
|
|
|
|
// way, or a scalar to be replicated. Decompress them to create a mapping from
|
|
|
|
// guest result components to the ones in the value vector.
|
|
|
|
uint32_t used_result_components = result.GetUsedResultComponents();
|
|
|
|
unsigned int result_unswizzled_value_components[4] = {};
|
|
|
|
if (value_num_components > 1) {
|
|
|
|
unsigned int value_component = 0;
|
|
|
|
uint32_t used_result_components_remaining = used_result_components;
|
|
|
|
uint32_t result_component;
|
|
|
|
while (xe::bit_scan_forward(used_result_components_remaining,
|
|
|
|
&result_component)) {
|
2020-11-01 10:12:31 +00:00
|
|
|
used_result_components_remaining &= ~(uint32_t(1) << result_component);
|
2020-10-29 19:07:02 +00:00
|
|
|
result_unswizzled_value_components[result_component] =
|
|
|
|
std::min(value_component++, value_num_components - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get swizzled mapping of non-constant components to the components of
|
|
|
|
// `value`.
|
|
|
|
unsigned int result_swizzled_value_components[4] = {};
|
|
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
|
|
if (!(non_constant_components & (1 << i))) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
SwizzleSource swizzle = result.components[i];
|
|
|
|
assert_true(swizzle >= SwizzleSource::kX && swizzle <= SwizzleSource::kW);
|
|
|
|
result_swizzled_value_components[i] =
|
|
|
|
result_unswizzled_value_components[uint32_t(swizzle) -
|
|
|
|
uint32_t(SwizzleSource::kX)];
|
|
|
|
}
|
|
|
|
|
|
|
|
spv::Id target_type = builder_->getDerefTypeId(target_pointer);
|
|
|
|
unsigned int target_num_components =
|
|
|
|
builder_->getNumTypeComponents(target_type);
|
|
|
|
assert_true(
|
|
|
|
target_num_components ==
|
|
|
|
GetInstructionStorageTargetUsedComponentCount(result.storage_target));
|
|
|
|
uint32_t target_component_mask = (1 << target_num_components) - 1;
|
|
|
|
assert_zero(used_write_mask & ~target_component_mask);
|
|
|
|
|
|
|
|
spv::Id value_to_store;
|
|
|
|
if (target_component_mask == used_write_mask) {
|
|
|
|
// All components are overwritten - no need to load the original value.
|
|
|
|
// Possible cases:
|
|
|
|
// * Non-constants only.
|
|
|
|
// * Vector target.
|
|
|
|
// * Vector source.
|
|
|
|
// * Identity swizzle - store directly.
|
|
|
|
// * Non-identity swizzle - shuffle.
|
|
|
|
// * Scalar source - smear.
|
|
|
|
// * Scalar target.
|
|
|
|
// * Vector source - extract.
|
|
|
|
// * Scalar source - store directly.
|
|
|
|
// * Constants only.
|
|
|
|
// * Vector target - make composite constant.
|
|
|
|
// * Scalar target - store directly.
|
|
|
|
// * Mixed non-constants and constants (only for vector targets - scalar
|
|
|
|
// targets fully covered by the previous cases).
|
|
|
|
// * Vector source - shuffle with {0, 1} also applying swizzle.
|
|
|
|
// * Scalar source - construct composite.
|
|
|
|
if (!constant_components) {
|
|
|
|
if (target_num_components > 1) {
|
|
|
|
if (value_num_components > 1) {
|
|
|
|
// Non-constants only - vector target, vector source.
|
|
|
|
bool is_identity_swizzle =
|
|
|
|
target_num_components == value_num_components;
|
|
|
|
for (uint32_t i = 0; is_identity_swizzle && i < target_num_components;
|
|
|
|
++i) {
|
|
|
|
is_identity_swizzle &= result_swizzled_value_components[i] == i;
|
|
|
|
}
|
|
|
|
if (is_identity_swizzle) {
|
|
|
|
value_to_store = value;
|
|
|
|
} else {
|
|
|
|
uint_vector_temp_util_.clear();
|
|
|
|
uint_vector_temp_util_.reserve(target_num_components);
|
|
|
|
uint_vector_temp_util_.insert(
|
|
|
|
uint_vector_temp_util_.cend(), result_swizzled_value_components,
|
|
|
|
result_swizzled_value_components + target_num_components);
|
|
|
|
value_to_store = builder_->createRvalueSwizzle(
|
|
|
|
spv::NoPrecision, target_type, value, uint_vector_temp_util_);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Non-constants only - vector target, scalar source.
|
|
|
|
value_to_store =
|
|
|
|
builder_->smearScalar(spv::NoPrecision, value, target_type);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (value_num_components > 1) {
|
|
|
|
// Non-constants only - scalar target, vector source.
|
|
|
|
value_to_store = builder_->createCompositeExtract(
|
|
|
|
value, type_float_, result_swizzled_value_components[0]);
|
|
|
|
} else {
|
|
|
|
// Non-constants only - scalar target, scalar source.
|
|
|
|
value_to_store = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (!non_constant_components) {
|
|
|
|
if (target_num_components > 1) {
|
|
|
|
// Constants only - vector target.
|
|
|
|
id_vector_temp_util_.clear();
|
|
|
|
id_vector_temp_util_.reserve(target_num_components);
|
|
|
|
for (uint32_t i = 0; i < target_num_components; ++i) {
|
|
|
|
id_vector_temp_util_.push_back(
|
|
|
|
(constant_values & (1 << i)) ? const_float_1_ : const_float_0_);
|
|
|
|
}
|
|
|
|
value_to_store =
|
|
|
|
builder_->makeCompositeConstant(target_type, id_vector_temp_util_);
|
|
|
|
} else {
|
|
|
|
// Constants only - scalar target.
|
|
|
|
value_to_store =
|
|
|
|
(constant_values & 0b0001) ? const_float_1_ : const_float_0_;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
assert_true(target_num_components > 1);
|
|
|
|
if (value_num_components > 1) {
|
|
|
|
// Mixed non-constants and constants - vector source.
|
|
|
|
std::unique_ptr<spv::Instruction> shuffle_op =
|
2020-10-31 14:56:46 +00:00
|
|
|
std::make_unique<spv::Instruction>(
|
|
|
|
builder_->getUniqueId(), target_type, spv::OpVectorShuffle);
|
2020-10-29 19:07:02 +00:00
|
|
|
shuffle_op->addIdOperand(value);
|
|
|
|
shuffle_op->addIdOperand(const_float2_0_1_);
|
|
|
|
for (uint32_t i = 0; i < target_num_components; ++i) {
|
|
|
|
shuffle_op->addImmediateOperand(
|
|
|
|
(constant_components & (1 << i))
|
|
|
|
? value_num_components + ((constant_values >> i) & 1)
|
|
|
|
: result_swizzled_value_components[i]);
|
|
|
|
}
|
2020-10-31 14:56:46 +00:00
|
|
|
value_to_store = shuffle_op->getResultId();
|
2020-10-29 19:07:02 +00:00
|
|
|
builder_->getBuildPoint()->addInstruction(std::move(shuffle_op));
|
|
|
|
} else {
|
|
|
|
// Mixed non-constants and constants - scalar source.
|
|
|
|
id_vector_temp_util_.clear();
|
|
|
|
id_vector_temp_util_.reserve(target_num_components);
|
|
|
|
for (uint32_t i = 0; i < target_num_components; ++i) {
|
|
|
|
if (constant_components & (1 << i)) {
|
|
|
|
id_vector_temp_util_.push_back(
|
|
|
|
(constant_values & (1 << i)) ? const_float_1_ : const_float_0_);
|
|
|
|
} else {
|
|
|
|
id_vector_temp_util_.push_back(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
value_to_store = builder_->createCompositeConstruct(
|
|
|
|
target_type, id_vector_temp_util_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Only certain components are overwritten.
|
|
|
|
// Scalar targets are always overwritten fully, can't reach this case for
|
|
|
|
// them.
|
|
|
|
assert_true(target_num_components > 1);
|
|
|
|
value_to_store = builder_->createLoad(target_pointer, spv::NoPrecision);
|
|
|
|
// Two steps:
|
|
|
|
// 1) Insert constants by shuffling (first so dependency chain of step 2 is
|
|
|
|
// simpler if constants are written first).
|
|
|
|
// 2) Insert value components - via shuffling for vector source, via
|
|
|
|
// composite inserts for scalar value.
|
|
|
|
if (constant_components) {
|
|
|
|
std::unique_ptr<spv::Instruction> shuffle_op =
|
2020-10-31 14:56:46 +00:00
|
|
|
std::make_unique<spv::Instruction>(builder_->getUniqueId(),
|
|
|
|
target_type, spv::OpVectorShuffle);
|
2020-10-29 19:07:02 +00:00
|
|
|
shuffle_op->addIdOperand(value_to_store);
|
|
|
|
shuffle_op->addIdOperand(const_float2_0_1_);
|
|
|
|
for (uint32_t i = 0; i < target_num_components; ++i) {
|
|
|
|
shuffle_op->addImmediateOperand((constant_components & (1 << i))
|
|
|
|
? target_num_components +
|
|
|
|
((constant_values >> i) & 1)
|
|
|
|
: i);
|
|
|
|
}
|
2020-10-31 14:56:46 +00:00
|
|
|
value_to_store = shuffle_op->getResultId();
|
2020-10-29 19:07:02 +00:00
|
|
|
builder_->getBuildPoint()->addInstruction(std::move(shuffle_op));
|
|
|
|
}
|
|
|
|
if (non_constant_components) {
|
|
|
|
if (value_num_components > 1) {
|
|
|
|
std::unique_ptr<spv::Instruction> shuffle_op =
|
2020-10-31 14:56:46 +00:00
|
|
|
std::make_unique<spv::Instruction>(
|
|
|
|
builder_->getUniqueId(), target_type, spv::OpVectorShuffle);
|
2020-10-29 19:07:02 +00:00
|
|
|
shuffle_op->addIdOperand(value_to_store);
|
|
|
|
shuffle_op->addIdOperand(value);
|
|
|
|
for (uint32_t i = 0; i < target_num_components; ++i) {
|
|
|
|
shuffle_op->addImmediateOperand(
|
|
|
|
(non_constant_components & (1 << i))
|
|
|
|
? target_num_components + result_swizzled_value_components[i]
|
|
|
|
: i);
|
|
|
|
}
|
2020-10-31 14:56:46 +00:00
|
|
|
value_to_store = shuffle_op->getResultId();
|
2020-10-29 19:07:02 +00:00
|
|
|
builder_->getBuildPoint()->addInstruction(std::move(shuffle_op));
|
|
|
|
} else {
|
|
|
|
for (uint32_t i = 0; i < target_num_components; ++i) {
|
|
|
|
if (non_constant_components & (1 << i)) {
|
|
|
|
value_to_store = builder_->createCompositeInsert(
|
|
|
|
value, value_to_store, target_type, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
builder_->createStore(value_to_store, target_pointer);
|
|
|
|
}
|
|
|
|
|
2020-11-03 20:31:52 +00:00
|
|
|
spv::Id SpirvShaderTranslator::EndianSwap32Uint(spv::Id value, spv::Id endian) {
|
|
|
|
spv::Id type = builder_->getTypeId(value);
|
|
|
|
spv::Id const_uint_8_scalar = builder_->makeUintConstant(8);
|
|
|
|
spv::Id const_uint_00ff00ff_scalar = builder_->makeUintConstant(0x00FF00FF);
|
|
|
|
spv::Id const_uint_16_scalar = builder_->makeUintConstant(16);
|
|
|
|
spv::Id const_uint_8_typed, const_uint_00ff00ff_typed, const_uint_16_typed;
|
|
|
|
int num_components = builder_->getNumTypeComponents(type);
|
|
|
|
if (num_components > 1) {
|
|
|
|
id_vector_temp_.reserve(num_components);
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.insert(id_vector_temp_.cend(), num_components,
|
|
|
|
const_uint_8_scalar);
|
|
|
|
const_uint_8_typed = builder_->makeCompositeConstant(type, id_vector_temp_);
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.insert(id_vector_temp_.cend(), num_components,
|
|
|
|
const_uint_00ff00ff_scalar);
|
|
|
|
const_uint_00ff00ff_typed =
|
|
|
|
builder_->makeCompositeConstant(type, id_vector_temp_);
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.insert(id_vector_temp_.cend(), num_components,
|
|
|
|
const_uint_16_scalar);
|
|
|
|
const_uint_16_typed =
|
|
|
|
builder_->makeCompositeConstant(type, id_vector_temp_);
|
|
|
|
} else {
|
|
|
|
const_uint_8_typed = const_uint_8_scalar;
|
|
|
|
const_uint_00ff00ff_typed = const_uint_00ff00ff_scalar;
|
|
|
|
const_uint_16_typed = const_uint_16_scalar;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 8-in-16 or one half of 8-in-32 (doing 8-in-16 swap).
|
|
|
|
spv::Id is_8in16 = builder_->createBinOp(
|
|
|
|
spv::OpIEqual, type_bool_, endian,
|
|
|
|
builder_->makeUintConstant(
|
|
|
|
static_cast<unsigned int>(xenos::Endian::k8in16)));
|
|
|
|
spv::Id is_8in32 = builder_->createBinOp(
|
|
|
|
spv::OpIEqual, type_bool_, endian,
|
|
|
|
builder_->makeUintConstant(
|
|
|
|
static_cast<unsigned int>(xenos::Endian::k8in32)));
|
|
|
|
spv::Id is_8in16_or_8in32 =
|
|
|
|
builder_->createBinOp(spv::OpLogicalAnd, type_bool_, is_8in16, is_8in32);
|
|
|
|
spv::Block& block_pre_8in16 = *builder_->getBuildPoint();
|
|
|
|
assert_false(block_pre_8in16.isTerminated());
|
|
|
|
spv::Block& block_8in16 = builder_->makeNewBlock();
|
|
|
|
spv::Block& block_8in16_merge = builder_->makeNewBlock();
|
|
|
|
SpirvCreateSelectionMerge(block_8in16_merge.getId());
|
|
|
|
builder_->createConditionalBranch(is_8in16_or_8in32, &block_8in16,
|
|
|
|
&block_8in16_merge);
|
|
|
|
builder_->setBuildPoint(&block_8in16);
|
|
|
|
spv::Id swapped_8in16 = builder_->createBinOp(
|
|
|
|
spv::OpBitwiseOr, type,
|
|
|
|
builder_->createBinOp(
|
|
|
|
spv::OpBitwiseAnd, type,
|
|
|
|
builder_->createBinOp(spv::OpShiftRightLogical, type, value,
|
|
|
|
const_uint_8_typed),
|
|
|
|
const_uint_00ff00ff_typed),
|
|
|
|
builder_->createBinOp(
|
|
|
|
spv::OpShiftLeftLogical, type,
|
|
|
|
builder_->createBinOp(spv::OpBitwiseAnd, type, value,
|
|
|
|
const_uint_00ff00ff_typed),
|
|
|
|
const_uint_8_typed));
|
|
|
|
builder_->createBranch(&block_8in16_merge);
|
|
|
|
builder_->setBuildPoint(&block_8in16_merge);
|
|
|
|
{
|
|
|
|
std::unique_ptr<spv::Instruction> phi_op =
|
|
|
|
std::make_unique<spv::Instruction>(builder_->getUniqueId(), type,
|
|
|
|
spv::OpPhi);
|
|
|
|
phi_op->addIdOperand(swapped_8in16);
|
|
|
|
phi_op->addIdOperand(block_8in16.getId());
|
|
|
|
phi_op->addIdOperand(value);
|
|
|
|
phi_op->addIdOperand(block_pre_8in16.getId());
|
|
|
|
value = phi_op->getResultId();
|
|
|
|
builder_->getBuildPoint()->addInstruction(std::move(phi_op));
|
|
|
|
}
|
|
|
|
|
|
|
|
// 16-in-32 or another half of 8-in-32 (doing 16-in-32 swap).
|
|
|
|
spv::Id is_16in32 = builder_->createBinOp(
|
|
|
|
spv::OpIEqual, type_bool_, endian,
|
|
|
|
builder_->makeUintConstant(
|
|
|
|
static_cast<unsigned int>(xenos::Endian::k16in32)));
|
|
|
|
spv::Id is_8in32_or_16in32 =
|
|
|
|
builder_->createBinOp(spv::OpLogicalAnd, type_bool_, is_8in32, is_16in32);
|
|
|
|
spv::Block& block_pre_16in32 = *builder_->getBuildPoint();
|
|
|
|
spv::Block& block_16in32 = builder_->makeNewBlock();
|
|
|
|
spv::Block& block_16in32_merge = builder_->makeNewBlock();
|
|
|
|
SpirvCreateSelectionMerge(block_16in32_merge.getId());
|
|
|
|
builder_->createConditionalBranch(is_8in32_or_16in32, &block_16in32,
|
|
|
|
&block_16in32_merge);
|
|
|
|
builder_->setBuildPoint(&block_16in32);
|
|
|
|
id_vector_temp_.clear();
|
|
|
|
id_vector_temp_.reserve(4);
|
|
|
|
id_vector_temp_.push_back(builder_->createBinOp(
|
|
|
|
spv::OpShiftRightLogical, type, value, const_uint_16_typed));
|
|
|
|
id_vector_temp_.push_back(value);
|
|
|
|
id_vector_temp_.insert(id_vector_temp_.cend(), 2,
|
|
|
|
builder_->makeIntConstant(16));
|
|
|
|
spv::Id swapped_16in32 =
|
|
|
|
builder_->createOp(spv::OpBitFieldInsert, type, id_vector_temp_);
|
|
|
|
builder_->createBranch(&block_16in32_merge);
|
|
|
|
builder_->setBuildPoint(&block_16in32_merge);
|
|
|
|
{
|
|
|
|
std::unique_ptr<spv::Instruction> phi_op =
|
|
|
|
std::make_unique<spv::Instruction>(builder_->getUniqueId(), type,
|
|
|
|
spv::OpPhi);
|
|
|
|
phi_op->addIdOperand(swapped_16in32);
|
|
|
|
phi_op->addIdOperand(block_16in32.getId());
|
|
|
|
phi_op->addIdOperand(value);
|
|
|
|
phi_op->addIdOperand(block_pre_16in32.getId());
|
|
|
|
value = phi_op->getResultId();
|
|
|
|
builder_->getBuildPoint()->addInstruction(std::move(phi_op));
|
|
|
|
}
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
2020-10-11 17:22:15 +00:00
|
|
|
} // namespace gpu
|
|
|
|
} // namespace xe
|