[SPIR-V] Texture fetch instructions

This commit is contained in:
Triang3l 2022-06-09 21:42:16 +03:00
parent f875a8d887
commit 1a22216e44
5 changed files with 2363 additions and 18 deletions

View File

@ -30,6 +30,7 @@ SpirvShaderTranslator::Features::Features(bool all)
max_storage_buffer_range(all ? UINT32_MAX : (128 * 1024 * 1024)),
clip_distance(all),
cull_distance(all),
image_view_format_swizzle(all),
signed_zero_inf_nan_preserve_float32(all),
denorm_flush_to_zero_float32(all) {}
@ -51,6 +52,15 @@ SpirvShaderTranslator::Features::Features(
} else {
spirv_version = spv::Spv_1_0;
}
const VkPhysicalDevicePortabilitySubsetFeaturesKHR*
device_portability_subset_features =
provider.device_portability_subset_features();
if (device_portability_subset_features) {
image_view_format_swizzle =
bool(device_portability_subset_features->imageViewFormatSwizzle);
} else {
image_view_format_swizzle = true;
}
if (spirv_version >= spv::Spv_1_4 ||
device_extensions.khr_shader_float_controls) {
const VkPhysicalDeviceFloatControlsPropertiesKHR&
@ -198,6 +208,14 @@ void SpirvShaderTranslator::StartTranslation() {
size_t offset;
spv::Id type;
};
spv::Id type_uint4_array_2 = builder_->makeArrayType(
type_uint4_, builder_->makeUintConstant(2), sizeof(uint32_t) * 4);
builder_->addDecoration(type_uint4_array_2, spv::DecorationArrayStride,
sizeof(uint32_t) * 4);
spv::Id type_uint4_array_4 = builder_->makeArrayType(
type_uint4_, builder_->makeUintConstant(4), sizeof(uint32_t) * 4);
builder_->addDecoration(type_uint4_array_4, spv::DecorationArrayStride,
sizeof(uint32_t) * 4);
const SystemConstant system_constants[] = {
{"flags", offsetof(SystemConstants, flags), type_uint_},
{"vertex_index_endian", offsetof(SystemConstants, vertex_index_endian),
@ -206,6 +224,10 @@ void SpirvShaderTranslator::StartTranslation() {
type_int_},
{"ndc_scale", offsetof(SystemConstants, ndc_scale), type_float3_},
{"ndc_offset", offsetof(SystemConstants, ndc_offset), type_float3_},
{"texture_swizzled_signs",
offsetof(SystemConstants, texture_swizzled_signs), type_uint4_array_2},
{"texture_swizzles", offsetof(SystemConstants, texture_swizzles),
type_uint4_array_4},
};
id_vector_temp_.clear();
id_vector_temp_.reserve(xe::countof(system_constants));
@ -401,6 +423,15 @@ void SpirvShaderTranslator::StartTranslation() {
var_main_vfetch_address_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassFunction, type_int_,
"xe_var_vfetch_address", const_int_0_);
var_main_tfetch_lod_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassFunction, type_float_,
"xe_var_tfetch_lod", const_float_0_);
var_main_tfetch_gradients_h_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassFunction, type_float3_,
"xe_var_tfetch_gradients_h", const_float3_0_);
var_main_tfetch_gradients_v_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassFunction, type_float3_,
"xe_var_tfetch_gradients_v", const_float3_0_);
uint32_t register_array_size = register_count();
if (register_array_size) {
id_vector_temp_.clear();
@ -945,6 +976,29 @@ void SpirvShaderTranslator::ProcessJumpInstruction(
builder_->createBranch(main_loop_continue_);
}
spv::Id SpirvShaderTranslator::SpirvSmearScalarResultOrConstant(
spv::Id scalar, spv::Id vector_type) {
bool is_constant = builder_->isConstant(scalar);
bool is_spec_constant = builder_->isSpecConstant(scalar);
if (!is_constant && !is_spec_constant) {
return builder_->smearScalar(spv::NoPrecision, scalar, vector_type);
}
assert_true(builder_->getTypeClass(builder_->getTypeId(scalar)) ==
builder_->getTypeClass(builder_->getScalarTypeId(vector_type)));
if (!builder_->isVectorType(vector_type)) {
assert_true(builder_->isScalarType(vector_type));
return scalar;
}
int num_components = builder_->getNumTypeComponents(vector_type);
id_vector_temp_util_.clear();
id_vector_temp_util_.reserve(size_t(num_components));
for (int i = 0; i < num_components; ++i) {
id_vector_temp_util_.push_back(scalar);
}
return builder_->makeCompositeConstant(vector_type, id_vector_temp_util_,
is_spec_constant);
}
void SpirvShaderTranslator::EnsureBuildPointAvailable() {
if (!builder_->getBuildPoint()->isTerminated()) {
return;
@ -2049,5 +2103,188 @@ spv::Id SpirvShaderTranslator::LoadUint32FromSharedMemory(
return value_phi_result;
}
spv::Id SpirvShaderTranslator::PWLGammaToLinear(spv::Id gamma,
bool gamma_pre_saturated) {
spv::Id value_type = builder_->getTypeId(gamma);
assert_true(builder_->isFloatType(builder_->getScalarTypeId(value_type)));
bool is_vector = builder_->isVectorType(value_type);
assert_true(is_vector || builder_->isFloatType(value_type));
int num_components = builder_->getNumTypeComponents(value_type);
assert_true(num_components < 4);
spv::Id bool_type = type_bool_vectors_[num_components - 1];
spv::Id const_vector_0 = const_float_vectors_0_[num_components - 1];
spv::Id const_vector_1 = SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(1.0f), value_type);
if (!gamma_pre_saturated) {
// Saturate, flushing NaN to 0.
id_vector_temp_.clear();
id_vector_temp_.reserve(3);
id_vector_temp_.push_back(gamma);
id_vector_temp_.push_back(const_vector_0);
id_vector_temp_.push_back(const_vector_1);
gamma = builder_->createBuiltinCall(value_type, ext_inst_glsl_std_450_,
GLSLstd450NClamp, id_vector_temp_);
}
spv::Id is_piece_at_least_3 = builder_->createBinOp(
spv::OpFOrdGreaterThanEqual, bool_type, gamma,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(192.0f / 255.0f), value_type));
spv::Id scale_3_or_2 = builder_->createTriOp(
spv::OpSelect, value_type, is_piece_at_least_3,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(8.0f / 1024.0f), value_type),
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(4.0f / 1024.0f), value_type));
spv::Id offset_3_or_2 = builder_->createTriOp(
spv::OpSelect, value_type, is_piece_at_least_3,
SpirvSmearScalarResultOrConstant(builder_->makeFloatConstant(-1024.0f),
value_type),
SpirvSmearScalarResultOrConstant(builder_->makeFloatConstant(-256.0f),
value_type));
spv::Id is_piece_at_least_1 = builder_->createBinOp(
spv::OpFOrdGreaterThanEqual, bool_type, gamma,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(64.0f / 255.0f), value_type));
spv::Id scale_1_or_0 = builder_->createTriOp(
spv::OpSelect, value_type, is_piece_at_least_1,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(2.0f / 1024.0f), value_type),
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(1.0f / 1024.0f), value_type));
spv::Id offset_1_or_0 = builder_->createTriOp(
spv::OpSelect, value_type, is_piece_at_least_1,
SpirvSmearScalarResultOrConstant(builder_->makeFloatConstant(-64.0f),
value_type),
const_vector_0);
spv::Id is_piece_at_least_2 = builder_->createBinOp(
spv::OpFOrdGreaterThanEqual, bool_type, gamma,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(96.0f / 255.0f), value_type));
spv::Id scale =
builder_->createTriOp(spv::OpSelect, value_type, is_piece_at_least_2,
scale_3_or_2, scale_1_or_0);
spv::Id offset =
builder_->createTriOp(spv::OpSelect, value_type, is_piece_at_least_2,
offset_3_or_2, offset_1_or_0);
spv::Op value_times_scalar_opcode =
is_vector ? spv::OpVectorTimesScalar : spv::OpFMul;
// linear = gamma * (255.0f * 1024.0f) * scale + offset
spv::Id linear =
builder_->createBinOp(value_times_scalar_opcode, value_type, gamma,
builder_->makeFloatConstant(255.0f * 1024.0f));
builder_->addDecoration(linear, spv::DecorationNoContraction);
linear = builder_->createBinOp(spv::OpFMul, value_type, linear, scale);
builder_->addDecoration(linear, spv::DecorationNoContraction);
linear = builder_->createBinOp(spv::OpFAdd, value_type, linear, offset);
builder_->addDecoration(linear, spv::DecorationNoContraction);
// linear += trunc(linear * scale)
spv::Id linear_integer_term =
builder_->createBinOp(spv::OpFMul, value_type, linear, scale);
builder_->addDecoration(linear_integer_term, spv::DecorationNoContraction);
id_vector_temp_.clear();
id_vector_temp_.push_back(linear_integer_term);
linear_integer_term = builder_->createBuiltinCall(
value_type, ext_inst_glsl_std_450_, GLSLstd450Trunc, id_vector_temp_);
linear = builder_->createBinOp(spv::OpFAdd, value_type, linear,
linear_integer_term);
builder_->addDecoration(linear, spv::DecorationNoContraction);
// linear *= 1.0f / 1023.0f
linear = builder_->createBinOp(value_times_scalar_opcode, value_type, linear,
builder_->makeFloatConstant(1.0f / 1023.0f));
builder_->addDecoration(linear, spv::DecorationNoContraction);
return linear;
}
spv::Id SpirvShaderTranslator::LinearToPWLGamma(spv::Id linear,
bool linear_pre_saturated) {
spv::Id value_type = builder_->getTypeId(linear);
assert_true(builder_->isFloatType(builder_->getScalarTypeId(value_type)));
bool is_vector = builder_->isVectorType(value_type);
assert_true(is_vector || builder_->isFloatType(value_type));
int num_components = builder_->getNumTypeComponents(value_type);
assert_true(num_components < 4);
spv::Id bool_type = type_bool_vectors_[num_components - 1];
spv::Id const_vector_0 = const_float_vectors_0_[num_components - 1];
spv::Id const_vector_1 = SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(1.0f), value_type);
if (!linear_pre_saturated) {
// Saturate, flushing NaN to 0.
id_vector_temp_.clear();
id_vector_temp_.reserve(3);
id_vector_temp_.push_back(linear);
id_vector_temp_.push_back(const_vector_0);
id_vector_temp_.push_back(const_vector_1);
linear = builder_->createBuiltinCall(value_type, ext_inst_glsl_std_450_,
GLSLstd450NClamp, id_vector_temp_);
}
spv::Id is_piece_at_least_3 = builder_->createBinOp(
spv::OpFOrdGreaterThanEqual, bool_type, linear,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(512.0f / 1023.0f), value_type));
spv::Id scale_3_or_2 = builder_->createTriOp(
spv::OpSelect, value_type, is_piece_at_least_3,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(1023.0f / 8.0f), value_type),
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(1023.0f / 4.0f), value_type));
spv::Id offset_3_or_2 = builder_->createTriOp(
spv::OpSelect, value_type, is_piece_at_least_3,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(128.0f / 255.0f), value_type),
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(64.0f / 255.0f), value_type));
spv::Id is_piece_at_least_1 = builder_->createBinOp(
spv::OpFOrdGreaterThanEqual, bool_type, linear,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(64.0f / 1023.0f), value_type));
spv::Id scale_1_or_0 = builder_->createTriOp(
spv::OpSelect, value_type, is_piece_at_least_1,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(1023.0f / 2.0f), value_type),
SpirvSmearScalarResultOrConstant(builder_->makeFloatConstant(1023.0f),
value_type));
spv::Id offset_1_or_0 = builder_->createTriOp(
spv::OpSelect, value_type, is_piece_at_least_1,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(32.0f / 255.0f), value_type),
const_vector_0);
spv::Id is_piece_at_least_2 = builder_->createBinOp(
spv::OpFOrdGreaterThanEqual, bool_type, linear,
SpirvSmearScalarResultOrConstant(
builder_->makeFloatConstant(128.0f / 1023.0f), value_type));
spv::Id scale =
builder_->createTriOp(spv::OpSelect, value_type, is_piece_at_least_2,
scale_3_or_2, scale_1_or_0);
spv::Id offset =
builder_->createTriOp(spv::OpSelect, value_type, is_piece_at_least_2,
offset_3_or_2, offset_1_or_0);
// gamma = trunc(linear * scale) * (1.0f / 255.0f) + offset
spv::Id gamma = builder_->createBinOp(spv::OpFMul, value_type, linear, scale);
builder_->addDecoration(gamma, spv::DecorationNoContraction);
id_vector_temp_.clear();
id_vector_temp_.push_back(gamma);
gamma = builder_->createBuiltinCall(value_type, ext_inst_glsl_std_450_,
GLSLstd450Trunc, id_vector_temp_);
gamma = builder_->createBinOp(
is_vector ? spv::OpVectorTimesScalar : spv::OpFMul, value_type, gamma,
builder_->makeFloatConstant(1.0f / 255.0f));
builder_->addDecoration(gamma, spv::DecorationNoContraction);
gamma = builder_->createBinOp(spv::OpFAdd, value_type, gamma, offset);
builder_->addDecoration(gamma, spv::DecorationNoContraction);
return gamma;
}
} // namespace gpu
} // namespace xe

View File

@ -84,6 +84,16 @@ class SpirvShaderTranslator : public ShaderTranslator {
float ndc_offset[3];
uint32_t padding_ndc_offset;
// Each byte contains post-swizzle TextureSign values for each of the needed
// components of each of the 32 used texture fetch constants.
uint32_t texture_swizzled_signs[8];
// If the imageViewFormatSwizzle portability subset is not supported, the
// component swizzle (taking both guest and host swizzles into account) to
// apply to the result directly in the shader code. In each uint32_t,
// swizzles for 2 texture fetch constants (in bits 0:11 and 12:23).
uint32_t texture_swizzles[16];
};
// The minimum limit for maxPerStageDescriptorStorageBuffers is 4, and for
@ -151,6 +161,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
uint32_t max_storage_buffer_range;
bool clip_distance;
bool cull_distance;
bool image_view_format_swizzle;
bool signed_zero_inf_nan_preserve_float32;
bool denorm_flush_to_zero_float32;
};
@ -244,7 +255,6 @@ class SpirvShaderTranslator : public ShaderTranslator {
xenos::FetchOpDimension dimension;
bool is_signed;
spv::Id type;
spv::Id variable;
};
@ -259,6 +269,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
};
// Builder helpers.
spv::Id SpirvSmearScalarResultOrConstant(spv::Id scalar, spv::Id vector_type);
void SpirvCreateSelectionMerge(
spv::Id merge_block_id, spv::SelectionControlMask selection_control_mask =
spv::SelectionControlMaskNone) {
@ -379,6 +390,10 @@ class SpirvShaderTranslator : public ShaderTranslator {
spv::Id LoadUint32FromSharedMemory(spv::Id address_dwords_int);
// The source may be a floating-point scalar or a vector.
spv::Id PWLGammaToLinear(spv::Id gamma, bool gamma_pre_saturated);
spv::Id LinearToPWLGamma(spv::Id linear, bool linear_pre_saturated);
size_t FindOrAddTextureBinding(uint32_t fetch_constant,
xenos::FetchOpDimension dimension,
bool is_signed);
@ -387,6 +402,24 @@ class SpirvShaderTranslator : public ShaderTranslator {
xenos::TextureFilter min_filter,
xenos::TextureFilter mip_filter,
xenos::AnisoFilter aniso_filter);
// `texture_parameters` need to be set up except for `sampler`, which will be
// set internally, optionally doing linear interpolation between the an
// existing value and the new one (the result location may be the same as for
// the first lerp endpoint, but not across signedness).
void SampleTexture(spv::Builder::TextureParameters& texture_parameters,
spv::ImageOperandsMask image_operands_mask,
spv::Id image_unsigned, spv::Id image_signed,
spv::Id sampler, spv::Id is_all_signed,
spv::Id is_any_signed, spv::Id& result_unsigned_out,
spv::Id& result_signed_out,
spv::Id lerp_factor = spv::NoResult,
spv::Id lerp_first_unsigned = spv::NoResult,
spv::Id lerp_first_signed = spv::NoResult);
// `texture_parameters` need to be set up except for `sampler`, which will be
// set internally.
spv::Id QueryTextureLod(spv::Builder::TextureParameters& texture_parameters,
spv::Id image_unsigned, spv::Id image_signed,
spv::Id sampler, spv::Id is_all_signed);
Features features_;
@ -473,6 +506,8 @@ class SpirvShaderTranslator : public ShaderTranslator {
kSystemConstantIndexVertexBaseIndex,
kSystemConstantNdcScale,
kSystemConstantNdcOffset,
kSystemConstantTextureSwizzledSigns,
kSystemConstantTextureSwizzles,
};
spv::Id uniform_system_constants_;
spv::Id uniform_float_constants_;
@ -522,6 +557,11 @@ class SpirvShaderTranslator : public ShaderTranslator {
// `base + index * stride` in dwords from the last vfetch_full as it may be
// needed by vfetch_mini - int.
spv::Id var_main_vfetch_address_;
// float.
spv::Id var_main_tfetch_lod_;
// float3.
spv::Id var_main_tfetch_gradients_h_;
spv::Id var_main_tfetch_gradients_v_;
// float4[register_count()].
spv::Id var_main_registers_;
// VS only - float3 (special exports).

File diff suppressed because it is too large Load Diff

View File

@ -1774,7 +1774,7 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
// Update system constants before uploading them.
UpdateSystemConstantValues(primitive_processing_result.host_index_endian,
viewport_info);
viewport_info, used_texture_mask);
// Update uniform buffers and descriptor sets after binding the pipeline with
// the new layout.
@ -2682,7 +2682,8 @@ void VulkanCommandProcessor::UpdateDynamicState(
}
void VulkanCommandProcessor::UpdateSystemConstantValues(
xenos::Endian index_endian, const draw_util::ViewportInfo& viewport_info) {
xenos::Endian index_endian, const draw_util::ViewportInfo& viewport_info,
uint32_t used_texture_mask) {
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
SCOPE_profile_cpu_f("gpu");
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
@ -2731,6 +2732,55 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
system_constants_.ndc_offset[i] = viewport_info.ndc_offset[i];
}
// Texture signedness / gamma.
{
uint32_t textures_remaining = used_texture_mask;
uint32_t texture_index;
while (xe::bit_scan_forward(textures_remaining, &texture_index)) {
textures_remaining &= ~(UINT32_C(1) << texture_index);
uint32_t& texture_signs_uint =
system_constants_.texture_swizzled_signs[texture_index >> 2];
uint32_t texture_signs_shift = 8 * (texture_index & 3);
uint8_t texture_signs =
texture_cache_->GetActiveTextureSwizzledSigns(texture_index);
uint32_t texture_signs_shifted = uint32_t(texture_signs)
<< texture_signs_shift;
uint32_t texture_signs_mask = (UINT32_C(1 << 8) - 1)
<< texture_signs_shift;
dirty |=
(texture_signs_uint & texture_signs_mask) != texture_signs_shifted;
texture_signs_uint =
(texture_signs_uint & ~texture_signs_mask) | texture_signs_shifted;
}
}
// Texture host swizzle in the shader.
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
const VkPhysicalDevicePortabilitySubsetFeaturesKHR*
device_portability_subset_features =
provider.device_portability_subset_features();
if (device_portability_subset_features &&
!device_portability_subset_features->imageViewFormatSwizzle) {
uint32_t textures_remaining = used_texture_mask;
uint32_t texture_index;
while (xe::bit_scan_forward(textures_remaining, &texture_index)) {
textures_remaining &= ~(UINT32_C(1) << texture_index);
uint32_t& texture_swizzles_uint =
system_constants_.texture_swizzles[texture_index >> 1];
uint32_t texture_swizzle_shift = 12 * (texture_index & 1);
uint32_t texture_swizzle =
texture_cache_->GetActiveTextureHostSwizzle(texture_index);
uint32_t texture_swizzle_shifted = uint32_t(texture_swizzle)
<< texture_swizzle_shift;
uint32_t texture_swizzle_mask = (UINT32_C(1 << 12) - 1)
<< texture_swizzle_shift;
dirty |= (texture_swizzles_uint & texture_swizzle_mask) !=
texture_swizzle_shifted;
texture_swizzles_uint = (texture_swizzles_uint & ~texture_swizzle_mask) |
texture_swizzle_shifted;
}
}
if (dirty) {
current_graphics_descriptor_set_values_up_to_date_ &=
~(UINT32_C(1) << SpirvShaderTranslator::kDescriptorSetSystemConstants);

View File

@ -432,7 +432,8 @@ class VulkanCommandProcessor : public CommandProcessor {
bool primitive_polygonal,
reg::RB_DEPTHCONTROL normalized_depth_control);
void UpdateSystemConstantValues(xenos::Endian index_endian,
const draw_util::ViewportInfo& viewport_info);
const draw_util::ViewportInfo& viewport_info,
uint32_t used_texture_mask);
bool UpdateBindings(const VulkanShader* vertex_shader,
const VulkanShader* pixel_shader);
// Allocates a descriptor set and fills the VkWriteDescriptorSet structure.