[SPIR-V] Store vfetch_full address in a variable

This commit is contained in:
Triang3l 2022-02-07 23:00:23 +03:00
parent e447cf6ed8
commit 8ccb00d03d
3 changed files with 74 additions and 51 deletions

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -396,6 +396,9 @@ void SpirvShaderTranslator::StartTranslation() {
var_main_previous_scalar_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassFunction, type_float_,
"xe_var_previous_scalar", const_float_0_);
var_main_vfetch_address_ = builder_->createVariable(
spv::NoPrecision, spv::StorageClassFunction, type_int_,
"xe_var_vfetch_address", const_int_0_);
uint32_t register_array_size = register_count();
if (register_array_size) {
id_vector_temp_.clear();

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2021 Ben Vanik. All rights reserved. *
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -439,6 +439,9 @@ class SpirvShaderTranslator : public ShaderTranslator {
spv::Id var_main_address_absolute_;
// float.
spv::Id var_main_previous_scalar_;
// `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_;
// float4[register_count()].
spv::Id var_main_registers_;
// VS only - float3 (special exports).

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -27,7 +27,9 @@ void SpirvShaderTranslator::ProcessVertexFetchInstruction(
uint32_t used_result_components = instr.result.GetUsedResultComponents();
uint32_t needed_words = xenos::GetVertexFormatNeededWords(
instr.attributes.data_format, used_result_components);
if (!needed_words) {
// If this is vfetch_full, the address may still be needed for vfetch_mini -
// don't exit before calculating the address.
if (!needed_words && instr.is_mini_fetch) {
// Nothing to load - just constant 0/1 writes, or the swizzle includes only
// components that don't exist in the format (writing zero instead of them).
// Unpacking assumes at least some word is needed.
@ -37,56 +39,71 @@ void SpirvShaderTranslator::ProcessVertexFetchInstruction(
EnsureBuildPointAvailable();
// Get the base address in dwords from the bits 2:31 of the first fetch
// constant word.
uint32_t fetch_constant_word_0_index = instr.operands[1].storage_index << 1;
id_vector_temp_.clear();
id_vector_temp_.reserve(3);
// The only element of the fetch constant buffer.
id_vector_temp_.push_back(const_int_0_);
// Vector index.
id_vector_temp_.push_back(
builder_->makeIntConstant(int(fetch_constant_word_0_index >> 2)));
// Component index.
id_vector_temp_.push_back(
builder_->makeIntConstant(int(fetch_constant_word_0_index & 3)));
spv::Id fetch_constant_word_0 = builder_->createLoad(
builder_->createAccessChain(spv::StorageClassUniform,
uniform_fetch_constants_, id_vector_temp_),
spv::NoPrecision);
// TODO(Triang3l): Verify the fetch constant type (that it's a vertex fetch,
// not a texture fetch) here instead of dropping draws with invalid vertex
// fetch constants on the CPU when proper bound checks are added - vfetch may
// be conditional, so fetch constants may also be used conditionally.
spv::Id address = builder_->createUnaryOp(
spv::OpBitcast, type_int_,
builder_->createBinOp(spv::OpShiftRightLogical, type_uint_,
fetch_constant_word_0,
builder_->makeUintConstant(2)));
if (instr.attributes.stride) {
// Convert the index to an integer by flooring or by rounding to the nearest
// (as floor(index + 0.5) because rounding to the nearest even makes no
// sense for addressing, both 1.5 and 2.5 would be 2).
// http://web.archive.org/web/20100302145413/http://msdn.microsoft.com:80/en-us/library/bb313960.aspx
spv::Id index = GetOperandComponents(LoadOperandStorage(instr.operands[0]),
instr.operands[0], 0b0001);
if (instr.attributes.is_index_rounded) {
index = builder_->createBinOp(spv::OpFAdd, type_float_, index,
builder_->makeFloatConstant(0.5f));
builder_->addDecoration(index, spv::DecorationNoContraction);
}
spv::Id address;
if (instr.is_mini_fetch) {
// `base + index * stride` loaded by vfetch_full.
address = builder_->createLoad(var_main_vfetch_address_, spv::NoPrecision);
} else {
// Get the base address in dwords from the bits 2:31 of the first fetch
// constant word.
id_vector_temp_.clear();
id_vector_temp_.push_back(index);
index = builder_->createUnaryOp(
spv::OpConvertFToS, type_int_,
builder_->createBuiltinCall(type_float_, ext_inst_glsl_std_450_,
GLSLstd450Floor, id_vector_temp_));
if (instr.attributes.stride > 1) {
index = builder_->createBinOp(
spv::OpIMul, type_int_, index,
builder_->makeIntConstant(int(instr.attributes.stride)));
id_vector_temp_.reserve(3);
// The only element of the fetch constant buffer.
id_vector_temp_.push_back(const_int_0_);
// Vector index.
id_vector_temp_.push_back(
builder_->makeIntConstant(int(fetch_constant_word_0_index >> 2)));
// Component index.
id_vector_temp_.push_back(
builder_->makeIntConstant(int(fetch_constant_word_0_index & 3)));
spv::Id fetch_constant_word_0 = builder_->createLoad(
builder_->createAccessChain(spv::StorageClassUniform,
uniform_fetch_constants_, id_vector_temp_),
spv::NoPrecision);
// TODO(Triang3l): Verify the fetch constant type (that it's a vertex fetch,
// not a texture fetch) here instead of dropping draws with invalid vertex
// fetch constants on the CPU when proper bound checks are added - vfetch
// may be conditional, so fetch constants may also be used conditionally.
address = builder_->createUnaryOp(
spv::OpBitcast, type_int_,
builder_->createBinOp(spv::OpShiftRightLogical, type_uint_,
fetch_constant_word_0,
builder_->makeUintConstant(2)));
if (instr.attributes.stride) {
// Convert the index to an integer by flooring or by rounding to the
// nearest (as floor(index + 0.5) because rounding to the nearest even
// makes no sense for addressing, both 1.5 and 2.5 would be 2).
spv::Id index = GetOperandComponents(
LoadOperandStorage(instr.operands[0]), instr.operands[0], 0b0001);
if (instr.attributes.is_index_rounded) {
index = builder_->createBinOp(spv::OpFAdd, type_float_, index,
builder_->makeFloatConstant(0.5f));
builder_->addDecoration(index, spv::DecorationNoContraction);
}
id_vector_temp_.clear();
id_vector_temp_.push_back(index);
index = builder_->createUnaryOp(
spv::OpConvertFToS, type_int_,
builder_->createBuiltinCall(type_float_, ext_inst_glsl_std_450_,
GLSLstd450Floor, id_vector_temp_));
if (instr.attributes.stride > 1) {
index = builder_->createBinOp(
spv::OpIMul, type_int_, index,
builder_->makeIntConstant(int(instr.attributes.stride)));
}
address = builder_->createBinOp(spv::OpIAdd, type_int_, address, index);
}
address = builder_->createBinOp(spv::OpIAdd, type_int_, address, index);
// Store the address for the subsequent vfetch_mini.
builder_->createStore(address, var_main_vfetch_address_);
}
if (!needed_words) {
// The vfetch_full address has been loaded for the subsequent vfetch_mini,
// but there's no data to load.
StoreResult(instr.result, spv::NoResult);
return;
}
// Load the needed words.