For more optimal usage of exports and the parameter cache on the host regardless of how effective the optimizations in the host GPU driver are. Also reserve space for Vulkan/Metal/D3D11-specific HostVertexShaderTypes to use one more bit for the host vertex shader type in the shader modification bits, so that won't have to be done in the future as that would require invalidating shader storages (which are invalidated by this commit) again.
536 lines
21 KiB
C++
536 lines
21 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2018 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "xenia/base/assert.h"
|
|
#include "xenia/base/math.h"
|
|
#include "xenia/gpu/draw_util.h"
|
|
#include "xenia/gpu/dxbc_shader_translator.h"
|
|
#include "xenia/gpu/texture_cache.h"
|
|
|
|
namespace xe {
|
|
namespace gpu {
|
|
using namespace ucode;
|
|
|
|
// TODO(Triang3l): Support sub-dword memexports (like k_8 in 58410B86). This
|
|
// would require four 128 MB R8_UINT UAVs due to
|
|
// D3D12_REQ_BUFFER_RESOURCE_TEXEL_COUNT_2_TO_EXP. Need to be careful with
|
|
// resource binding tiers, however. Resource binding tier 1 on feature level
|
|
// 11_0 allows only 8 UAVs _across all stages_. RWByteAddressBuffer + 4 typed
|
|
// buffers is 5 per stage already, would need 10 for both VS and PS, or even 11
|
|
// with the eDRAM ROV. Need to drop draw commands doing memexport in both VS and
|
|
// PS on FL 11_0 resource binding tier 1.
|
|
|
|
void DxbcShaderTranslator::ExportToMemory_PackFixed32(
|
|
const uint32_t* eM_temps, uint32_t eM_count, const uint32_t bits[4],
|
|
const dxbc::Src& is_integer, const dxbc::Src& is_signed) {
|
|
// Will insert with BFI - sign extension of red will be overwritten, not
|
|
// truncated.
|
|
assert_not_zero(bits[0]);
|
|
assert_true(bits[0] + bits[1] + bits[2] + bits[3] == 32);
|
|
uint32_t mask = 0;
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
if (bits[i]) {
|
|
mask |= 1 << i;
|
|
}
|
|
}
|
|
a_.OpIf(true, is_signed);
|
|
{
|
|
float range[4];
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
range[i] = bits[i] ? float((uint32_t(1) << (bits[i] - 1)) - 1) : 0.0f;
|
|
}
|
|
dxbc::Src range_src(dxbc::Src::LP(range));
|
|
a_.OpIf(false, is_integer);
|
|
for (uint32_t i = 0; i < eM_count; ++i) {
|
|
uint32_t eM_temp = eM_temps[i];
|
|
a_.OpMul(dxbc::Dest::R(eM_temp, mask), dxbc::Src::R(eM_temp), range_src);
|
|
}
|
|
a_.OpEndIf();
|
|
for (uint32_t i = 0; i < eM_count; ++i) {
|
|
dxbc::Dest eM_dest(dxbc::Dest::R(eM_temps[i], mask));
|
|
dxbc::Src eM_src(dxbc::Src::R(eM_temps[i]));
|
|
// TODO(Triang3l): NaN should become zero, not -range.
|
|
a_.OpMax(eM_dest, eM_src, -range_src);
|
|
a_.OpMin(eM_dest, eM_src, range_src);
|
|
}
|
|
}
|
|
a_.OpElse();
|
|
{
|
|
float range[4];
|
|
for (uint32_t i = 0; i < 4; ++i) {
|
|
range[i] = float((uint32_t(1) << bits[i]) - 1);
|
|
}
|
|
dxbc::Src range_src(dxbc::Src::LP(range));
|
|
a_.OpIf(false, is_integer);
|
|
for (uint32_t i = 0; i < eM_count; ++i) {
|
|
uint32_t eM_temp = eM_temps[i];
|
|
a_.OpMul(dxbc::Dest::R(eM_temp, mask), dxbc::Src::R(eM_temp), range_src);
|
|
}
|
|
a_.OpEndIf();
|
|
for (uint32_t i = 0; i < eM_count; ++i) {
|
|
dxbc::Dest eM_dest(dxbc::Dest::R(eM_temps[i], mask));
|
|
dxbc::Src eM_src(dxbc::Src::R(eM_temps[i]));
|
|
a_.OpMax(eM_dest, eM_src, dxbc::Src::LF(0.0f));
|
|
a_.OpMin(eM_dest, eM_src, range_src);
|
|
}
|
|
}
|
|
a_.OpEndIf();
|
|
for (uint32_t i = 0; i < eM_count; ++i) {
|
|
uint32_t eM_temp = eM_temps[i];
|
|
// Round to the nearest integer, according to the rules of handling integer
|
|
// formats in Direct3D.
|
|
// TODO(Triang3l): Round by adding +-0.5, not with round_ne.
|
|
a_.OpRoundNE(dxbc::Dest::R(eM_temp, mask), dxbc::Src::R(eM_temp));
|
|
a_.OpFToI(dxbc::Dest::R(eM_temp, mask), dxbc::Src::R(eM_temp));
|
|
dxbc::Dest eM_packed_dest(dxbc::Dest::R(eM_temp, 0b0001));
|
|
dxbc::Src eM_packed_src(dxbc::Src::R(eM_temp, dxbc::Src::kXXXX));
|
|
uint32_t offset = bits[0];
|
|
for (uint32_t j = 1; j < 4; ++j) {
|
|
if (!bits[j]) {
|
|
continue;
|
|
}
|
|
a_.OpBFI(eM_packed_dest, dxbc::Src::LU(bits[j]), dxbc::Src::LU(offset),
|
|
dxbc::Src::R(eM_temp).Select(j), eM_packed_src);
|
|
offset += bits[j];
|
|
}
|
|
}
|
|
}
|
|
|
|
void DxbcShaderTranslator::ExportToMemory() {
|
|
if (system_temp_memexport_written_ == UINT32_MAX) {
|
|
// No exports in the shader.
|
|
return;
|
|
}
|
|
|
|
// Allocate a register for temporary values at various stages.
|
|
uint32_t control_temp = PushSystemTemp();
|
|
|
|
// Safety check if the shared memory is bound as UAV.
|
|
a_.OpUBFE(dxbc::Dest::R(control_temp, 0b0001), dxbc::Src::LU(1),
|
|
dxbc::Src::LU(kSysFlag_SharedMemoryIsUAV_Shift),
|
|
LoadFlagsSystemConstant());
|
|
// Open the `if` with the uniform condition for the shared memory buffer being
|
|
// bound as a UAV (more fine-grained checks are vector and likely divergent).
|
|
a_.OpIf(true, dxbc::Src::R(control_temp, dxbc::Src::kXXXX));
|
|
|
|
// Check more fine-grained limitations.
|
|
bool inner_condition_provided = false;
|
|
if (is_pixel_shader()) {
|
|
uint32_t resolution_scaled_axes =
|
|
uint32_t(draw_resolution_scale_x_ > 1) |
|
|
(uint32_t(draw_resolution_scale_y_ > 1) << 1);
|
|
if (resolution_scaled_axes) {
|
|
// Only do memexport for one host pixel in a guest pixel - prefer the
|
|
// host pixel closer to the center of the guest pixel, but one that's
|
|
// covered with the half-pixel offset according to the top-left rule (1
|
|
// for 2x because 0 isn't covered with the half-pixel offset, 1 for 3x
|
|
// because it's the center and is covered with the half-pixel offset too).
|
|
// Using control_temp.yz as per-axis temporary variables.
|
|
in_position_used_ |= resolution_scaled_axes;
|
|
a_.OpFToU(dxbc::Dest::R(control_temp, resolution_scaled_axes << 1),
|
|
dxbc::Src::V1D(in_reg_ps_position_, 0b0100 << 2));
|
|
a_.OpUDiv(dxbc::Dest::Null(),
|
|
dxbc::Dest::R(control_temp, resolution_scaled_axes << 1),
|
|
dxbc::Src::R(control_temp, 0b1001 << 2),
|
|
dxbc::Src::LU(0, draw_resolution_scale_x_,
|
|
draw_resolution_scale_y_, 0));
|
|
for (uint32_t i = 0; i < 2; ++i) {
|
|
if (!(resolution_scaled_axes & (1 << i))) {
|
|
continue;
|
|
}
|
|
// If there's no inner condition in control_temp.x yet, the condition
|
|
// for the current axis can go directly to it. Otherwise, need to merge
|
|
// with the previous condition, using control_temp.y or .z as an
|
|
// intermediate variable.
|
|
dxbc::Src resolution_scaled_axis_src(
|
|
dxbc::Src::R(control_temp).Select(1 + i));
|
|
a_.OpIEq(
|
|
dxbc::Dest::R(control_temp,
|
|
inner_condition_provided ? 1 << (1 + i) : 0b0001),
|
|
resolution_scaled_axis_src,
|
|
dxbc::Src::LU(
|
|
(i ? draw_resolution_scale_y_ : draw_resolution_scale_x_) >>
|
|
1));
|
|
if (inner_condition_provided) {
|
|
// Merge with the previous condition in control_temp.x.
|
|
a_.OpAnd(dxbc::Dest::R(control_temp, 0b0001),
|
|
dxbc::Src::R(control_temp, dxbc::Src::kXXXX),
|
|
resolution_scaled_axis_src);
|
|
}
|
|
inner_condition_provided = true;
|
|
}
|
|
}
|
|
// With sample-rate shading (with float24 conversion), only do memexport
|
|
// from one sample (as the shader is invoked multiple times for a pixel),
|
|
// if SV_SampleIndex == firstbit_lo(SV_Coverage). For zero coverage,
|
|
// firstbit_lo returns 0xFFFFFFFF.
|
|
if (IsSampleRate()) {
|
|
a_.OpFirstBitLo(dxbc::Dest::R(control_temp, 0b0010),
|
|
dxbc::Src::VCoverage());
|
|
a_.OpIEq(
|
|
dxbc::Dest::R(control_temp,
|
|
inner_condition_provided ? 0b0010 : 0b0001),
|
|
dxbc::Src::V1D(in_reg_ps_front_face_sample_index_, dxbc::Src::kYYYY),
|
|
dxbc::Src::R(control_temp, dxbc::Src::kYYYY));
|
|
if (inner_condition_provided) {
|
|
// Merge with the previous condition in control_temp.x.
|
|
a_.OpAnd(dxbc::Dest::R(control_temp, 0b0001),
|
|
dxbc::Src::R(control_temp, dxbc::Src::kXXXX),
|
|
dxbc::Src::R(control_temp, dxbc::Src::kYYYY));
|
|
}
|
|
inner_condition_provided = true;
|
|
}
|
|
}
|
|
// Open the inner (vector) conditional if needed.
|
|
if (inner_condition_provided) {
|
|
a_.OpIf(true, dxbc::Src::R(control_temp, dxbc::Src::kXXXX));
|
|
}
|
|
// control_temp.x is now free.
|
|
|
|
for (uint32_t i = 0; i < Shader::kMaxMemExports; ++i) {
|
|
uint32_t eA_temp = system_temps_memexport_address_[i];
|
|
if (eA_temp == UINT32_MAX) {
|
|
// Export not used.
|
|
continue;
|
|
}
|
|
// For simplicity of access, gather actually used eM# registers for this
|
|
// export. Zero-initialize eM_offsets because excess elements of it may be
|
|
// accessed, for stable caching.
|
|
uint32_t eM_temps[5], eM_offsets[5] = {}, eM_count = 0;
|
|
for (uint32_t j = 0; j < 5; ++j) {
|
|
uint32_t eM_temp = system_temps_memexport_data_[i][j];
|
|
if (eM_temp == UINT32_MAX) {
|
|
continue;
|
|
}
|
|
eM_temps[eM_count] = eM_temp;
|
|
eM_offsets[eM_count] = j;
|
|
++eM_count;
|
|
}
|
|
if (eM_count == 0) {
|
|
continue;
|
|
}
|
|
|
|
// Swap red and blue if needed.
|
|
a_.OpAnd(dxbc::Dest::R(control_temp, 0b0001),
|
|
dxbc::Src::R(eA_temp, dxbc::Src::kZZZZ),
|
|
dxbc::Src::LU(uint32_t(1) << 19));
|
|
for (uint32_t j = 0; j < eM_count; ++j) {
|
|
uint32_t eM_temp = eM_temps[j];
|
|
a_.OpMovC(dxbc::Dest::R(eM_temp, 0b0101),
|
|
dxbc::Src::R(control_temp, dxbc::Src::kXXXX),
|
|
dxbc::Src::R(eM_temp, 0b000010), dxbc::Src::R(eM_temp));
|
|
}
|
|
|
|
// Initialize element size in control_temp.x to 4 bytes as this is the most
|
|
// common size.
|
|
dxbc::Dest element_size_dest(dxbc::Dest::R(control_temp, 0b0001));
|
|
dxbc::Src element_size_src(dxbc::Src::R(control_temp, dxbc::Src::kXXXX));
|
|
a_.OpMov(element_size_dest, dxbc::Src::LU(4));
|
|
|
|
// Each eM should get a packed value in the destination format now.
|
|
|
|
// Extract format properties to control_temp.
|
|
// Y - signedness if fixed-point.
|
|
// Z - fractional/integer if fixed-point.
|
|
// W - color format.
|
|
a_.OpUBFE(dxbc::Dest::R(control_temp, 0b1110), dxbc::Src::LU(0, 1, 1, 6),
|
|
dxbc::Src::LU(0, 16, 17, 8),
|
|
dxbc::Src::R(eA_temp, dxbc::Src::kZZZZ));
|
|
dxbc::Src is_signed(dxbc::Src::R(control_temp, dxbc::Src::kYYYY));
|
|
dxbc::Src is_integer(dxbc::Src::R(control_temp, dxbc::Src::kZZZZ));
|
|
// Convert and pack the format.
|
|
a_.OpSwitch(dxbc::Src::R(control_temp, dxbc::Src::kWWWW));
|
|
// control_temp.w is now free.
|
|
{
|
|
// k_8_8_8_8
|
|
// k_8_8_8_8_AS_16_16_16_16
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::ColorFormat::k_8_8_8_8)));
|
|
a_.OpCase(dxbc::Src::LU(
|
|
uint32_t(xenos::ColorFormat::k_8_8_8_8_AS_16_16_16_16)));
|
|
{
|
|
uint32_t bits[4] = {8, 8, 8, 8};
|
|
ExportToMemory_PackFixed32(eM_temps, eM_count, bits, is_integer,
|
|
is_signed);
|
|
}
|
|
a_.OpBreak();
|
|
|
|
// k_2_10_10_10
|
|
// k_2_10_10_10_AS_16_16_16_16
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::ColorFormat::k_2_10_10_10)));
|
|
a_.OpCase(dxbc::Src::LU(
|
|
uint32_t(xenos::ColorFormat::k_2_10_10_10_AS_16_16_16_16)));
|
|
{
|
|
uint32_t bits[4] = {10, 10, 10, 2};
|
|
ExportToMemory_PackFixed32(eM_temps, eM_count, bits, is_integer,
|
|
is_signed);
|
|
}
|
|
a_.OpBreak();
|
|
|
|
// k_10_11_11
|
|
// k_10_11_11_AS_16_16_16_16
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::ColorFormat::k_10_11_11)));
|
|
a_.OpCase(dxbc::Src::LU(
|
|
uint32_t(xenos::ColorFormat::k_10_11_11_AS_16_16_16_16)));
|
|
{
|
|
uint32_t bits[4] = {11, 11, 10};
|
|
ExportToMemory_PackFixed32(eM_temps, eM_count, bits, is_integer,
|
|
is_signed);
|
|
}
|
|
a_.OpBreak();
|
|
|
|
// k_11_11_10
|
|
// k_11_11_10_AS_16_16_16_16
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::ColorFormat::k_11_11_10)));
|
|
a_.OpCase(dxbc::Src::LU(
|
|
uint32_t(xenos::ColorFormat::k_11_11_10_AS_16_16_16_16)));
|
|
{
|
|
uint32_t bits[4] = {10, 11, 11};
|
|
ExportToMemory_PackFixed32(eM_temps, eM_count, bits, is_integer,
|
|
is_signed);
|
|
}
|
|
a_.OpBreak();
|
|
|
|
// k_16_16
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::ColorFormat::k_16_16)));
|
|
{
|
|
uint32_t bits[4] = {16, 16};
|
|
ExportToMemory_PackFixed32(eM_temps, eM_count, bits, is_integer,
|
|
is_signed);
|
|
}
|
|
a_.OpBreak();
|
|
|
|
// k_16_16_16_16
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::ColorFormat::k_16_16_16_16)));
|
|
a_.OpMov(element_size_dest, dxbc::Src::LU(8));
|
|
a_.OpIf(true, is_signed);
|
|
{
|
|
a_.OpIf(false, is_integer);
|
|
for (uint32_t j = 0; j < eM_count; ++j) {
|
|
uint32_t eM_temp = eM_temps[j];
|
|
a_.OpMul(dxbc::Dest::R(eM_temp), dxbc::Src::R(eM_temp),
|
|
dxbc::Src::LF(32767.0f));
|
|
}
|
|
a_.OpEndIf();
|
|
for (uint32_t j = 0; j < eM_count; ++j) {
|
|
dxbc::Dest eM_dest(dxbc::Dest::R(eM_temps[j]));
|
|
dxbc::Src eM_src(dxbc::Src::R(eM_temps[j]));
|
|
// TODO(Triang3l): NaN should become zero, not -range.
|
|
a_.OpMax(eM_dest, eM_src, dxbc::Src::LF(-32767.0f));
|
|
a_.OpMin(eM_dest, eM_src, dxbc::Src::LF(32767.0f));
|
|
}
|
|
}
|
|
a_.OpElse();
|
|
{
|
|
a_.OpIf(false, is_integer);
|
|
for (uint32_t j = 0; j < eM_count; ++j) {
|
|
uint32_t eM_temp = eM_temps[j];
|
|
a_.OpMul(dxbc::Dest::R(eM_temp), dxbc::Src::R(eM_temp),
|
|
dxbc::Src::LF(65535.0f));
|
|
}
|
|
a_.OpEndIf();
|
|
for (uint32_t j = 0; j < eM_count; ++j) {
|
|
dxbc::Dest eM_dest(dxbc::Dest::R(eM_temps[j]));
|
|
dxbc::Src eM_src(dxbc::Src::R(eM_temps[j]));
|
|
a_.OpMax(eM_dest, eM_src, dxbc::Src::LF(0.0f));
|
|
a_.OpMin(eM_dest, eM_src, dxbc::Src::LF(65535.0f));
|
|
}
|
|
}
|
|
a_.OpEndIf();
|
|
for (uint32_t j = 0; j < eM_count; ++j) {
|
|
uint32_t eM_temp = eM_temps[j];
|
|
// Round to the nearest integer, according to the rules of handling
|
|
// integer formats in Direct3D.
|
|
// TODO(Triang3l): Round by adding +-0.5, not with round_ne.
|
|
a_.OpRoundNE(dxbc::Dest::R(eM_temp), dxbc::Src::R(eM_temp));
|
|
a_.OpFToI(dxbc::Dest::R(eM_temp), dxbc::Src::R(eM_temp));
|
|
a_.OpBFI(dxbc::Dest::R(eM_temp, 0b0011), dxbc::Src::LU(16),
|
|
dxbc::Src::LU(16), dxbc::Src::R(eM_temp, 0b1101),
|
|
dxbc::Src::R(eM_temp, 0b1000));
|
|
}
|
|
a_.OpBreak();
|
|
|
|
// k_16_16_FLOAT
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::ColorFormat::k_16_16_FLOAT)));
|
|
for (uint32_t j = 0; j < eM_count; ++j) {
|
|
uint32_t eM_temp = eM_temps[j];
|
|
a_.OpF32ToF16(dxbc::Dest::R(eM_temp, 0b0011), dxbc::Src::R(eM_temp));
|
|
a_.OpBFI(dxbc::Dest::R(eM_temp, 0b0001), dxbc::Src::LU(16),
|
|
dxbc::Src::LU(16), dxbc::Src::R(eM_temp, dxbc::Src::kYYYY),
|
|
dxbc::Src::R(eM_temp, dxbc::Src::kXXXX));
|
|
}
|
|
a_.OpBreak();
|
|
|
|
// k_16_16_16_16_FLOAT
|
|
a_.OpCase(
|
|
dxbc::Src::LU(uint32_t(xenos::ColorFormat::k_16_16_16_16_FLOAT)));
|
|
a_.OpMov(element_size_dest, dxbc::Src::LU(8));
|
|
for (uint32_t j = 0; j < eM_count; ++j) {
|
|
uint32_t eM_temp = eM_temps[j];
|
|
a_.OpF32ToF16(dxbc::Dest::R(eM_temp), dxbc::Src::R(eM_temp));
|
|
a_.OpBFI(dxbc::Dest::R(eM_temp, 0b0011), dxbc::Src::LU(16),
|
|
dxbc::Src::LU(16), dxbc::Src::R(eM_temp, 0b1101),
|
|
dxbc::Src::R(eM_temp, 0b1000));
|
|
}
|
|
a_.OpBreak();
|
|
|
|
// k_32_FLOAT
|
|
// Already in the destination format, 4 bytes per element already
|
|
// selected.
|
|
|
|
// k_32_32_FLOAT
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::ColorFormat::k_32_32_FLOAT)));
|
|
a_.OpMov(element_size_dest, dxbc::Src::LU(8));
|
|
// Already in the destination format.
|
|
a_.OpBreak();
|
|
|
|
// k_32_32_32_32_FLOAT
|
|
a_.OpCase(
|
|
dxbc::Src::LU(uint32_t(xenos::ColorFormat::k_32_32_32_32_FLOAT)));
|
|
a_.OpMov(element_size_dest, dxbc::Src::LU(16));
|
|
// Already in the destination format.
|
|
a_.OpBreak();
|
|
}
|
|
a_.OpEndSwitch();
|
|
// control_temp.yz are now free.
|
|
|
|
// Do endian swap.
|
|
{
|
|
dxbc::Dest endian_dest(dxbc::Dest::R(control_temp, 0b0010));
|
|
dxbc::Src endian_src(dxbc::Src::R(control_temp, dxbc::Src::kYYYY));
|
|
// Extract endianness into control_temp.y.
|
|
a_.OpAnd(endian_dest, dxbc::Src::R(eA_temp, dxbc::Src::kZZZZ),
|
|
dxbc::Src::LU(0b111));
|
|
|
|
// Change 8-in-64 and 8-in-128 to 8-in-32.
|
|
for (uint32_t j = 0; j < 2; ++j) {
|
|
a_.OpIEq(dxbc::Dest::R(control_temp, 0b0100), endian_src,
|
|
dxbc::Src::LU(uint32_t(j ? xenos::Endian128::k8in128
|
|
: xenos::Endian128::k8in64)));
|
|
for (uint32_t k = 0; k < eM_count; ++k) {
|
|
uint32_t eM_temp = eM_temps[k];
|
|
a_.OpMovC(dxbc::Dest::R(eM_temp),
|
|
dxbc::Src::R(control_temp, dxbc::Src::kZZZZ),
|
|
dxbc::Src::R(eM_temp, j ? 0b00011011 : 0b10110001),
|
|
dxbc::Src::R(eM_temp));
|
|
}
|
|
a_.OpMovC(endian_dest, dxbc::Src::R(control_temp, dxbc::Src::kZZZZ),
|
|
dxbc::Src::LU(uint32_t(xenos::Endian128::k8in32)),
|
|
endian_src);
|
|
}
|
|
|
|
uint32_t swap_temp = PushSystemTemp();
|
|
dxbc::Dest swap_temp_dest(dxbc::Dest::R(swap_temp));
|
|
dxbc::Src swap_temp_src(dxbc::Src::R(swap_temp));
|
|
|
|
// 8-in-16 or one half of 8-in-32.
|
|
a_.OpSwitch(endian_src);
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::Endian128::k8in16)));
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::Endian128::k8in32)));
|
|
for (uint32_t j = 0; j < eM_count; ++j) {
|
|
dxbc::Dest eM_dest(dxbc::Dest::R(eM_temps[j]));
|
|
dxbc::Src eM_src(dxbc::Src::R(eM_temps[j]));
|
|
// Temp = X0Z0.
|
|
a_.OpAnd(swap_temp_dest, eM_src, dxbc::Src::LU(0x00FF00FF));
|
|
// eM = YZW0.
|
|
a_.OpUShR(eM_dest, eM_src, dxbc::Src::LU(8));
|
|
// eM = Y0W0.
|
|
a_.OpAnd(eM_dest, eM_src, dxbc::Src::LU(0x00FF00FF));
|
|
// eM = YXWZ.
|
|
a_.OpUMAd(eM_dest, swap_temp_src, dxbc::Src::LU(256), eM_src);
|
|
}
|
|
a_.OpBreak();
|
|
a_.OpEndSwitch();
|
|
|
|
// 16-in-32 or another half of 8-in-32.
|
|
a_.OpSwitch(endian_src);
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::Endian128::k8in32)));
|
|
a_.OpCase(dxbc::Src::LU(uint32_t(xenos::Endian128::k16in32)));
|
|
for (uint32_t j = 0; j < eM_count; ++j) {
|
|
dxbc::Dest eM_dest(dxbc::Dest::R(eM_temps[j]));
|
|
dxbc::Src eM_src(dxbc::Src::R(eM_temps[j]));
|
|
// Temp = ZW00.
|
|
a_.OpUShR(swap_temp_dest, eM_src, dxbc::Src::LU(16));
|
|
// eM = ZWXY.
|
|
a_.OpBFI(eM_dest, dxbc::Src::LU(16), dxbc::Src::LU(16), eM_src,
|
|
swap_temp_src);
|
|
}
|
|
a_.OpBreak();
|
|
a_.OpEndSwitch();
|
|
|
|
// Release swap_temp.
|
|
PopSystemTemp();
|
|
}
|
|
// control_temp.yz are now free.
|
|
|
|
dxbc::Dest address_dest(dxbc::Dest::R(eA_temp, 0b0001));
|
|
dxbc::Src address_src(dxbc::Src::R(eA_temp, dxbc::Src::kXXXX));
|
|
// Multiply the base address by dword size, also dropping the 0x40000000
|
|
// bit.
|
|
a_.OpIShL(address_dest, address_src, dxbc::Src::LU(2));
|
|
// Drop the exponent in the element index.
|
|
a_.OpAnd(dxbc::Dest::R(eA_temp, 0b0010),
|
|
dxbc::Src::R(eA_temp, dxbc::Src::kYYYY),
|
|
dxbc::Src::LU((1 << 23) - 1));
|
|
// Add the offset of the first written element to the base address.
|
|
a_.OpUMAd(address_dest, dxbc::Src::R(eA_temp, dxbc::Src::kYYYY),
|
|
element_size_src, address_src);
|
|
// Do the writes.
|
|
dxbc::Src eM_written_src(
|
|
dxbc::Src::R(system_temp_memexport_written_).Select(i >> 2));
|
|
uint32_t eM_written_base = 1u << ((i & 3) << 3);
|
|
for (uint32_t j = 0; j < eM_count; ++j) {
|
|
// Go to the next eM#.
|
|
uint32_t eM_relative_offset = eM_offsets[j] - (j ? eM_offsets[j - 1] : 0);
|
|
if (eM_relative_offset) {
|
|
if (eM_relative_offset == 1) {
|
|
a_.OpIAdd(address_dest, element_size_src, address_src);
|
|
} else {
|
|
a_.OpUMAd(address_dest, dxbc::Src::LU(eM_relative_offset),
|
|
element_size_src, address_src);
|
|
}
|
|
}
|
|
// Check if the eM# was actually written to on the execution path.
|
|
a_.OpAnd(dxbc::Dest::R(control_temp, 0b0010), eM_written_src,
|
|
dxbc::Src::LU(eM_written_base << eM_offsets[j]));
|
|
a_.OpIf(true, dxbc::Src::R(control_temp, dxbc::Src::kYYYY));
|
|
// Write the element of the needed size.
|
|
dxbc::Src eM_src(dxbc::Src::R(eM_temps[j]));
|
|
a_.OpSwitch(element_size_src);
|
|
for (uint32_t k = 1; k <= 4; k <<= 1) {
|
|
a_.OpCase(dxbc::Src::LU(k * 4));
|
|
if (uav_index_shared_memory_ == kBindingIndexUnallocated) {
|
|
uav_index_shared_memory_ = uav_count_++;
|
|
}
|
|
a_.OpStoreRaw(
|
|
dxbc::Dest::U(uav_index_shared_memory_,
|
|
uint32_t(UAVRegister::kSharedMemory), (1 << k) - 1),
|
|
address_src, eM_src);
|
|
a_.OpBreak();
|
|
}
|
|
a_.OpEndSwitch();
|
|
a_.OpEndIf();
|
|
}
|
|
// control_temp.y is now free.
|
|
}
|
|
|
|
// Close the inner memexport possibility conditional.
|
|
if (inner_condition_provided) {
|
|
a_.OpEndIf();
|
|
}
|
|
|
|
// Close the outer memexport possibility conditional.
|
|
a_.OpEndIf();
|
|
|
|
// Release control_temp.
|
|
PopSystemTemp();
|
|
}
|
|
|
|
} // namespace gpu
|
|
} // namespace xe
|