[Vulkan] Basic framebuffer output

This commit is contained in:
Triang3l
2021-06-19 14:16:24 +03:00
parent 2c3141fc87
commit 270469d4d4
8 changed files with 1081 additions and 122 deletions

View File

@@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2021 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@@ -13,6 +13,7 @@
#include <cstddef>
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>
@@ -63,6 +64,9 @@ SpirvShaderTranslator::Features::Features(
}
}
const std::string SpirvShaderTranslator::kInterpolatorNamePrefix =
"xe_interpolator_";
SpirvShaderTranslator::SpirvShaderTranslator(const Features& features)
: features_(features) {}
@@ -363,6 +367,8 @@ void SpirvShaderTranslator::StartTranslation() {
if (is_vertex_shader()) {
StartVertexOrTessEvalShaderBeforeMain();
} else if (is_pixel_shader()) {
StartFragmentShaderBeforeMain();
}
// Begin the main function.
@@ -394,8 +400,9 @@ void SpirvShaderTranslator::StartTranslation() {
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.
// TODO(Triang3l): In PS, only need to initialize starting from the
// interpolators, probably manually. But likely not very important - the
// compiler in the driver will likely eliminate that write.
for (uint32_t i = 0; i < register_array_size; ++i) {
id_vector_temp_.push_back(const_float4_0_);
}
@@ -411,6 +418,8 @@ void SpirvShaderTranslator::StartTranslation() {
// main function.
if (is_vertex_shader()) {
StartVertexOrTessEvalShaderInMain();
} else if (is_pixel_shader()) {
StartFragmentShaderInMain();
}
// Open the main loop.
@@ -921,6 +930,16 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderBeforeMain() {
main_interface_.push_back(input_vertex_index_);
}
// Create the Xenia-specific outputs.
for (uint32_t i = 0; i < xenos::kMaxInterpolators; ++i) {
spv::Id interpolator = builder_->createVariable(
spv::NoPrecision, spv::StorageClassOutput, type_float4_,
(kInterpolatorNamePrefix + std::to_string(i)).c_str());
input_output_interpolators_[i] = interpolator;
builder_->addDecoration(interpolator, spv::DecorationLocation, int(i));
main_interface_.push_back(interpolator);
}
// 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
@@ -978,6 +997,11 @@ void SpirvShaderTranslator::StartVertexOrTessEvalShaderInMain() {
spv::NoPrecision, spv::StorageClassFunction, type_float3_,
"xe_var_point_size_edge_flag_kill_vertex");
// Zero the interpolators.
for (uint32_t i = 0; i < xenos::kMaxInterpolators; ++i) {
builder_->createStore(const_float4_0_, input_output_interpolators_[i]);
}
// Load the vertex index or the tessellation parameters.
if (register_count()) {
// TODO(Triang3l): Barycentric coordinates and patch index.
@@ -1167,6 +1191,73 @@ void SpirvShaderTranslator::CompleteVertexOrTessEvalShaderInMain() {
id_vector_temp_));
}
void SpirvShaderTranslator::StartFragmentShaderBeforeMain() {
// Interpolator inputs.
uint32_t interpolator_count =
std::min(xenos::kMaxInterpolators, register_count());
for (uint32_t i = 0; i < interpolator_count; ++i) {
spv::Id interpolator = builder_->createVariable(
spv::NoPrecision, spv::StorageClassInput, type_float4_,
(kInterpolatorNamePrefix + std::to_string(i)).c_str());
input_output_interpolators_[i] = interpolator;
builder_->addDecoration(interpolator, spv::DecorationLocation, int(i));
main_interface_.push_back(interpolator);
}
// Framebuffer attachment outputs.
std::fill(output_fragment_data_.begin(), output_fragment_data_.end(),
spv::NoResult);
static const char* const kFragmentDataNames[] = {
"xe_out_fragment_data_0",
"xe_out_fragment_data_1",
"xe_out_fragment_data_2",
"xe_out_fragment_data_3",
};
uint32_t shader_writes_color_targets =
current_shader().writes_color_targets();
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
if (!(shader_writes_color_targets & (uint32_t(1) << i))) {
continue;
}
spv::Id output_fragment_data_rt =
builder_->createVariable(spv::NoPrecision, spv::StorageClassOutput,
type_float4_, kFragmentDataNames[i]);
output_fragment_data_[i] = output_fragment_data_rt;
builder_->addDecoration(output_fragment_data_rt, spv::DecorationLocation,
int(i));
// Make invariant as pixel shaders may be used for various precise
// computations.
builder_->addDecoration(output_fragment_data_rt, spv::DecorationInvariant);
main_interface_.push_back(output_fragment_data_rt);
}
}
void SpirvShaderTranslator::StartFragmentShaderInMain() {
// Copy the interpolators to general-purpose registers.
// TODO(Triang3l): Centroid.
// TODO(Triang3l): ps_param_gen.
uint32_t interpolator_count =
std::min(xenos::kMaxInterpolators, register_count());
for (uint32_t i = 0; i < interpolator_count; ++i) {
id_vector_temp_.clear();
// Register array element.
id_vector_temp_.push_back(builder_->makeIntConstant(int(i)));
builder_->createStore(
builder_->createLoad(input_output_interpolators_[i], spv::NoPrecision),
builder_->createAccessChain(spv::StorageClassFunction,
var_main_registers_, id_vector_temp_));
}
// Initialize the colors for safety.
uint32_t shader_writes_color_targets =
current_shader().writes_color_targets();
for (uint32_t i = 0; i < xenos::kMaxColorRenderTargets; ++i) {
if (shader_writes_color_targets & (uint32_t(1) << i)) {
builder_->createStore(const_float4_0_, output_fragment_data_[i]);
}
}
}
void SpirvShaderTranslator::UpdateExecConditionals(
ParsedExecInstruction::Type type, uint32_t bool_constant_index,
bool condition) {
@@ -1507,6 +1598,10 @@ void SpirvShaderTranslator::StoreResult(const InstructionResult& result,
target_pointer = builder_->createAccessChain(
spv::StorageClassFunction, var_main_registers_, id_vector_temp_util_);
} break;
case InstructionStorageTarget::kInterpolator:
assert_true(is_vertex_shader());
target_pointer = input_output_interpolators_[result.storage_index];
break;
case InstructionStorageTarget::kPosition:
assert_true(is_vertex_shader());
id_vector_temp_util_.clear();
@@ -1515,6 +1610,13 @@ void SpirvShaderTranslator::StoreResult(const InstructionResult& result,
target_pointer = builder_->createAccessChain(
spv::StorageClassOutput, output_per_vertex_, id_vector_temp_util_);
break;
case InstructionStorageTarget::kColor:
assert_true(is_pixel_shader());
assert_not_zero(used_write_mask);
assert_true(current_shader().writes_color_target(result.storage_index));
target_pointer = output_fragment_data_[result.storage_index];
assert_true(target_pointer != spv::NoResult);
break;
default:
// TODO(Triang3l): All storage targets.
break;