Files
Xenia-Canary/src/xenia/gpu/ucode.h
2018-11-22 21:01:05 +03:00

1488 lines
46 KiB
C++

/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2015 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_GPU_UCODE_H_
#define XENIA_GPU_UCODE_H_
#include <cstdint>
#include "xenia/base/assert.h"
#include "xenia/base/platform.h"
#include "xenia/gpu/xenos.h"
// Closest AMD doc:
// https://developer.amd.com/wordpress/media/2012/10/R600_Instruction_Set_Architecture.pdf
// Microcode format differs, but most fields/enums are the same.
// This code comes from the freedreno project:
// https://github.com/freedreno/freedreno/blob/master/includes/instr-a2xx.h
/*
* Copyright (c) 2012 Rob Clark <robdclark@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
namespace xe {
namespace gpu {
namespace ucode {
// Defines control flow opcodes used to schedule instructions.
enum class ControlFlowOpcode : uint32_t {
// No-op - used to fill space.
kNop = 0,
// Executes fetch or ALU instructions.
kExec = 1,
// Executes fetch or ALU instructions then ends execution.
kExecEnd = 2,
// Conditionally executes based on a bool const.
kCondExec = 3,
// Conditionally executes based on a bool const then ends execution.
kCondExecEnd = 4,
// Conditionally executes based on the current predicate.
kCondExecPred = 5,
// Conditionally executes based on the current predicate then ends execution.
kCondExecPredEnd = 6,
// Starts a loop that must be terminated with kLoopEnd.
kLoopStart = 7,
// Continues or breaks out of a loop started with kLoopStart.
kLoopEnd = 8,
// Conditionally calls a function.
// A return address is pushed to the stack to be used by a kReturn.
kCondCall = 9,
// Returns from the current function as called by kCondCall.
// This is a no-op if not in a function.
kReturn = 10,
// Conditionally jumps to an arbitrary address based on a bool const.
kCondJmp = 11,
// Allocates output values.
kAlloc = 12,
// Conditionally executes based on the current predicate.
// Optionally resets the predicate value.
kCondExecPredClean = 13,
// Conditionally executes based on the current predicate then ends execution.
// Optionally resets the predicate value.
kCondExecPredCleanEnd = 14,
// Hints that no more vertex fetches will be performed.
kMarkVsFetchDone = 15,
};
// Returns true if the given control flow opcode executes ALU or fetch
// instructions.
constexpr bool IsControlFlowOpcodeExec(ControlFlowOpcode opcode) {
return opcode == ControlFlowOpcode::kExec ||
opcode == ControlFlowOpcode::kExecEnd ||
opcode == ControlFlowOpcode::kCondExec ||
opcode == ControlFlowOpcode::kCondExecEnd ||
opcode == ControlFlowOpcode::kCondExecPred ||
opcode == ControlFlowOpcode::kCondExecPredEnd ||
opcode == ControlFlowOpcode::kCondExecPredClean ||
opcode == ControlFlowOpcode::kCondExecPredCleanEnd;
}
// Returns true if the given control flow opcode terminates the shader after
// executing.
constexpr bool DoesControlFlowOpcodeEndShader(ControlFlowOpcode opcode) {
return opcode == ControlFlowOpcode::kExecEnd ||
opcode == ControlFlowOpcode::kCondExecEnd ||
opcode == ControlFlowOpcode::kCondExecPredEnd ||
opcode == ControlFlowOpcode::kCondExecPredCleanEnd;
}
// Returns true if the given control flow opcode resets the predicate prior to
// execution.
constexpr bool DoesControlFlowOpcodeCleanPredicate(ControlFlowOpcode opcode) {
return opcode == ControlFlowOpcode::kCondExecPredClean ||
opcode == ControlFlowOpcode::kCondExecPredCleanEnd;
}
// Determines whether addressing is based on a0 or aL.
enum class AddressingMode : uint32_t {
// Indexing into register sets is done based on aL.
// This allows forms like c[aL + 5].
kRelative = 0,
// Indexing into register sets is done based on a0.
// This allows forms like c[a0 + 5].
kAbsolute = 1,
};
// Defines the type of a ControlFlowOpcode::kAlloc instruction.
// The allocation is just a size reservation and there may be multiple in a
// shader.
enum class AllocType : uint32_t {
// ?
kNone = 0,
// Vertex shader exports a position.
kVsPosition = 1,
// Vertex shader exports interpolators.
kVsInterpolators = 2,
// Pixel shader exports colors.
kPsColors = 2,
// MEMEXPORT?
kMemory = 3,
};
// Instruction data for ControlFlowOpcode::kExec and kExecEnd.
struct ControlFlowExecInstruction {
ControlFlowOpcode opcode() const {
return static_cast<ControlFlowOpcode>(opcode_);
}
AddressingMode addressing_mode() const {
return static_cast<AddressingMode>(address_mode_);
}
// Address of the instructions to execute.
uint32_t address() const { return address_; }
// Number of instructions being executed.
uint32_t count() const { return count_; }
// Sequence bits, 2 per instruction, indicating whether ALU or fetch.
uint32_t sequence() const { return serialize_; }
// Whether to reset the current predicate.
bool clean() const { return clean_ == 1; }
// ?
bool is_yield() const { return is_yeild_ == 1; }
private:
// Word 0: (32 bits)
uint32_t address_ : 12;
uint32_t count_ : 3;
uint32_t is_yeild_ : 1;
uint32_t serialize_ : 12;
uint32_t vc_hi_ : 4; // Vertex cache?
// Word 1: (16 bits)
uint32_t vc_lo_ : 2;
uint32_t : 7;
uint32_t clean_ : 1;
uint32_t : 1;
uint32_t address_mode_ : 1;
uint32_t opcode_ : 4;
};
static_assert_size(ControlFlowExecInstruction, 8);
// Instruction data for ControlFlowOpcode::kCondExec and kCondExecEnd.
struct ControlFlowCondExecInstruction {
ControlFlowOpcode opcode() const {
return static_cast<ControlFlowOpcode>(opcode_);
}
AddressingMode addressing_mode() const {
return static_cast<AddressingMode>(address_mode_);
}
// Address of the instructions to execute.
uint32_t address() const { return address_; }
// Number of instructions being executed.
uint32_t count() const { return count_; }
// Sequence bits, 2 per instruction, indicating whether ALU or fetch.
uint32_t sequence() const { return serialize_; }
// Constant index used as the conditional.
uint32_t bool_address() const { return bool_address_; }
// Required condition value of the comparision (true or false).
bool condition() const { return condition_ == 1; }
// ?
bool is_yield() const { return is_yeild_ == 1; }
private:
// Word 0: (32 bits)
uint32_t address_ : 12;
uint32_t count_ : 3;
uint32_t is_yeild_ : 1;
uint32_t serialize_ : 12;
uint32_t vc_hi_ : 4; // Vertex cache?
// Word 1: (16 bits)
uint32_t vc_lo_ : 2;
uint32_t bool_address_ : 8;
uint32_t condition_ : 1;
uint32_t address_mode_ : 1;
uint32_t opcode_ : 4;
};
static_assert_size(ControlFlowCondExecInstruction, 8);
// Instruction data for ControlFlowOpcode::kCondExecPred, kCondExecPredEnd,
// kCondExecPredClean, kCondExecPredCleanEnd.
struct ControlFlowCondExecPredInstruction {
ControlFlowOpcode opcode() const {
return static_cast<ControlFlowOpcode>(opcode_);
}
AddressingMode addressing_mode() const {
return static_cast<AddressingMode>(address_mode_);
}
// Address of the instructions to execute.
uint32_t address() const { return address_; }
// Number of instructions being executed.
uint32_t count() const { return count_; }
// Sequence bits, 2 per instruction, indicating whether ALU or fetch.
uint32_t sequence() const { return serialize_; }
// Whether to reset the current predicate.
bool clean() const { return clean_ == 1; }
// Required condition value of the comparision (true or false).
bool condition() const { return condition_ == 1; }
// ?
bool is_yield() const { return is_yeild_ == 1; }
private:
// Word 0: (32 bits)
uint32_t address_ : 12;
uint32_t count_ : 3;
uint32_t is_yeild_ : 1;
uint32_t serialize_ : 12;
uint32_t vc_hi_ : 4; // Vertex cache?
// Word 1: (16 bits)
uint32_t vc_lo_ : 2;
uint32_t : 7;
uint32_t clean_ : 1;
uint32_t condition_ : 1;
uint32_t address_mode_ : 1;
uint32_t opcode_ : 4;
};
static_assert_size(ControlFlowCondExecPredInstruction, 8);
// Instruction data for ControlFlowOpcode::kLoopStart.
struct ControlFlowLoopStartInstruction {
ControlFlowOpcode opcode() const {
return static_cast<ControlFlowOpcode>(opcode_);
}
AddressingMode addressing_mode() const {
return static_cast<AddressingMode>(address_mode_);
}
// Target address to jump to when skipping the loop.
uint32_t address() const { return address_; }
// Whether to reuse the current aL instead of reset it to loop start.
bool is_repeat() const { return is_repeat_; }
// Integer constant register that holds the loop parameters.
// Byte-wise: [loop count, start, step [-128, 127], ?]
uint32_t loop_id() const { return loop_id_; }
private:
// Word 0: (32 bits)
uint32_t address_ : 13;
uint32_t is_repeat_ : 1;
uint32_t : 2;
uint32_t loop_id_ : 5;
uint32_t : 11;
// Word 1: (16 bits)
uint32_t : 11;
uint32_t address_mode_ : 1;
uint32_t opcode_ : 4;
};
static_assert_size(ControlFlowLoopStartInstruction, 8);
// Instruction data for ControlFlowOpcode::kLoopEnd.
struct ControlFlowLoopEndInstruction {
ControlFlowOpcode opcode() const {
return static_cast<ControlFlowOpcode>(opcode_);
}
AddressingMode addressing_mode() const {
return static_cast<AddressingMode>(address_mode_);
}
// Target address of the start of the loop body.
uint32_t address() const { return address_; }
// Integer constant register that holds the loop parameters.
// Byte-wise: [loop count, start, step [-128, 127], ?]
uint32_t loop_id() const { return loop_id_; }
// Break from the loop if the predicate matches the expected value.
bool is_predicated_break() const { return is_predicated_break_; }
// Required condition value of the comparision (true or false).
bool condition() const { return condition_ == 1; }
private:
// Word 0: (32 bits)
uint32_t address_ : 13;
uint32_t : 3;
uint32_t loop_id_ : 5;
uint32_t is_predicated_break_ : 1;
uint32_t : 10;
// Word 1: (16 bits)
uint32_t : 10;
uint32_t condition_ : 1;
uint32_t address_mode_ : 1;
uint32_t opcode_ : 4;
};
static_assert_size(ControlFlowLoopEndInstruction, 8);
// Instruction data for ControlFlowOpcode::kCondCall.
struct ControlFlowCondCallInstruction {
ControlFlowOpcode opcode() const {
return static_cast<ControlFlowOpcode>(opcode_);
}
AddressingMode addressing_mode() const {
return static_cast<AddressingMode>(address_mode_);
}
// Target address.
uint32_t address() const { return address_; }
// Unconditional call - ignores condition/predication.
bool is_unconditional() const { return is_unconditional_; }
// Whether the call is predicated (or conditional).
bool is_predicated() const { return is_predicated_; }
// Constant index used as the conditional.
uint32_t bool_address() const { return bool_address_; }
// Required condition value of the comparision (true or false).
bool condition() const { return condition_ == 1; }
private:
// Word 0: (32 bits)
uint32_t address_ : 13;
uint32_t is_unconditional_ : 1;
uint32_t is_predicated_ : 1;
uint32_t : 17;
// Word 1: (16 bits)
uint32_t : 2;
uint32_t bool_address_ : 8;
uint32_t condition_ : 1;
uint32_t address_mode_ : 1;
uint32_t opcode_ : 4;
};
static_assert_size(ControlFlowCondCallInstruction, 8);
// Instruction data for ControlFlowOpcode::kReturn.
struct ControlFlowReturnInstruction {
ControlFlowOpcode opcode() const {
return static_cast<ControlFlowOpcode>(opcode_);
}
AddressingMode addressing_mode() const {
return static_cast<AddressingMode>(address_mode_);
}
private:
// Word 0: (32 bits)
uint32_t : 32;
// Word 1: (16 bits)
uint32_t : 11;
AddressingMode address_mode_ : 1;
ControlFlowOpcode opcode_ : 4;
};
static_assert_size(ControlFlowReturnInstruction, 8);
// Instruction data for ControlFlowOpcode::kCondJmp.
struct ControlFlowCondJmpInstruction {
ControlFlowOpcode opcode() const {
return static_cast<ControlFlowOpcode>(opcode_);
}
AddressingMode addressing_mode() const {
return static_cast<AddressingMode>(address_mode_);
}
// Target address.
uint32_t address() const { return address_; }
// Unconditional jump - ignores condition/predication.
bool is_unconditional() const { return is_unconditional_; }
// Whether the jump is predicated (or conditional).
bool is_predicated() const { return is_predicated_; }
// Constant index used as the conditional.
uint32_t bool_address() const { return bool_address_; }
// Required condition value of the comparision (true or false).
bool condition() const { return condition_ == 1; }
private:
// Word 0: (32 bits)
uint32_t address_ : 13;
uint32_t is_unconditional_ : 1;
uint32_t is_predicated_ : 1;
uint32_t : 17;
// Word 1: (16 bits)
uint32_t : 1;
uint32_t direction_ : 1;
uint32_t bool_address_ : 8;
uint32_t condition_ : 1;
uint32_t address_mode_ : 1;
uint32_t opcode_ : 4;
};
static_assert_size(ControlFlowCondJmpInstruction, 8);
// Instruction data for ControlFlowOpcode::kAlloc.
struct ControlFlowAllocInstruction {
ControlFlowOpcode opcode() const {
return static_cast<ControlFlowOpcode>(opcode_);
}
// The total number of the given type allocated by this instruction.
uint32_t size() const { return size_; }
// Unconditional jump - ignores condition/predication.
AllocType alloc_type() const { return static_cast<AllocType>(alloc_type_); }
private:
// Word 0: (32 bits)
uint32_t size_ : 3;
uint32_t : 29;
// Word 1: (16 bits)
uint32_t : 8;
uint32_t is_unserialized_ : 1;
uint32_t alloc_type_ : 2;
uint32_t : 1;
uint32_t opcode_ : 4;
};
static_assert_size(ControlFlowAllocInstruction, 8);
XEPACKEDUNION(ControlFlowInstruction, {
ControlFlowOpcode opcode() const {
return static_cast<ControlFlowOpcode>(opcode_value);
}
ControlFlowExecInstruction exec; // kExec*
ControlFlowCondExecInstruction cond_exec; // kCondExec*
ControlFlowCondExecPredInstruction cond_exec_pred; // kCondExecPred*
ControlFlowLoopStartInstruction loop_start; // kLoopStart
ControlFlowLoopEndInstruction loop_end; // kLoopEnd
ControlFlowCondCallInstruction cond_call; // kCondCall
ControlFlowReturnInstruction ret; // kReturn
ControlFlowCondJmpInstruction cond_jmp; // kCondJmp
ControlFlowAllocInstruction alloc; // kAlloc
XEPACKEDSTRUCTANONYMOUS({
uint32_t unused_0 : 32;
uint32_t unused_1 : 12;
uint32_t opcode_value : 4;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t dword_0;
uint32_t dword_1;
});
});
static_assert_size(ControlFlowInstruction, 8);
inline void UnpackControlFlowInstructions(const uint32_t* dwords,
ControlFlowInstruction* out_a,
ControlFlowInstruction* out_b) {
uint32_t dword_0 = dwords[0];
uint32_t dword_1 = dwords[1];
uint32_t dword_2 = dwords[2];
out_a->dword_0 = dword_0;
out_a->dword_1 = dword_1 & 0xFFFF;
out_b->dword_0 = (dword_1 >> 16) | (dword_2 << 16);
out_b->dword_1 = dword_2 >> 16;
}
enum class FetchOpcode {
kVertexFetch = 0,
kTextureFetch = 1,
kGetTextureBorderColorFrac = 16,
kGetTextureComputedLod = 17,
kGetTextureGradients = 18,
kGetTextureWeights = 19,
kSetTextureLod = 24,
kSetTextureGradientsHorz = 25,
kSetTextureGradientsVert = 26,
kUnknownTextureOp = 27,
};
struct VertexFetchInstruction {
FetchOpcode opcode() const {
return static_cast<FetchOpcode>(data_.opcode_value);
}
// Whether the jump is predicated (or conditional).
bool is_predicated() const { return data_.is_predicated; }
// Required condition value of the comparision (true or false).
bool predicate_condition() const { return data_.pred_condition == 1; }
// Vertex fetch constant index [0-95].
uint32_t fetch_constant_index() const {
return data_.const_index * 3 + data_.const_index_sel;
}
uint32_t dest() const { return data_.dst_reg; }
uint32_t dest_swizzle() const { return data_.dst_swiz; }
bool is_dest_relative() const { return data_.dst_reg_am; }
uint32_t src() const { return data_.src_reg; }
uint32_t src_swizzle() const { return data_.src_swiz; }
bool is_src_relative() const { return data_.src_reg_am; }
// Returns true if the fetch actually fetches data.
// This may be false if it's used only to populate constants.
bool fetches_any_data() const {
uint32_t dst_swiz = data_.dst_swiz;
bool fetches_any_data = false;
for (int i = 0; i < 4; i++) {
if ((dst_swiz & 0x7) == 4) {
// 0.0
} else if ((dst_swiz & 0x7) == 5) {
// 1.0
} else if ((dst_swiz & 0x7) == 6) {
// ?
} else if ((dst_swiz & 0x7) == 7) {
// Previous register value.
} else {
fetches_any_data = true;
break;
}
dst_swiz >>= 3;
}
return fetches_any_data;
}
uint32_t prefetch_count() const { return data_.prefetch_count; }
bool is_mini_fetch() const { return data_.is_mini_fetch == 1; }
VertexFormat data_format() const {
return static_cast<VertexFormat>(data_.format);
}
// [-32, 31]
int exp_adjust() const {
return ((static_cast<int>(data_.exp_adjust) << 26) >> 26);
}
bool is_signed() const { return data_.fomat_comp_all == 1; }
bool is_normalized() const { return data_.num_format_all == 0; }
bool is_index_rounded() const { return data_.is_index_rounded == 1; }
// Dword stride, [0-255].
uint32_t stride() const { return data_.stride; }
// Dword offset, [
uint32_t offset() const { return data_.offset; }
void AssignFromFull(const VertexFetchInstruction& full) {
data_.stride = full.data_.stride;
data_.const_index = full.data_.const_index;
data_.const_index_sel = full.data_.const_index_sel;
}
private:
XEPACKEDSTRUCT(Data, {
XEPACKEDSTRUCTANONYMOUS({
uint32_t opcode_value : 5;
uint32_t src_reg : 6;
uint32_t src_reg_am : 1;
uint32_t dst_reg : 6;
uint32_t dst_reg_am : 1;
uint32_t must_be_one : 1;
uint32_t const_index : 5;
uint32_t const_index_sel : 2;
uint32_t prefetch_count : 3;
uint32_t src_swiz : 2;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t dst_swiz : 12;
uint32_t fomat_comp_all : 1;
uint32_t num_format_all : 1;
uint32_t signed_rf_mode_all : 1;
uint32_t is_index_rounded : 1;
uint32_t format : 6;
uint32_t reserved2 : 2;
uint32_t exp_adjust : 6;
uint32_t is_mini_fetch : 1;
uint32_t is_predicated : 1;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t stride : 8;
uint32_t offset : 23;
uint32_t pred_condition : 1;
});
});
Data data_;
};
struct TextureFetchInstruction {
FetchOpcode opcode() const {
return static_cast<FetchOpcode>(data_.opcode_value);
}
// Whether the jump is predicated (or conditional).
bool is_predicated() const { return data_.is_predicated; }
// Required condition value of the comparision (true or false).
bool predicate_condition() const { return data_.pred_condition == 1; }
// Texture fetch constant index [0-31].
uint32_t fetch_constant_index() const { return data_.const_index; }
uint32_t dest() const { return data_.dst_reg; }
uint32_t dest_swizzle() const { return data_.dst_swiz; }
bool is_dest_relative() const { return data_.dst_reg_am; }
uint32_t src() const { return data_.src_reg; }
uint32_t src_swizzle() const { return data_.src_swiz; }
bool is_src_relative() const { return data_.src_reg_am; }
TextureDimension dimension() const {
return static_cast<TextureDimension>(data_.dimension);
}
bool fetch_valid_only() const { return data_.fetch_valid_only == 1; }
bool unnormalized_coordinates() const { return data_.tx_coord_denorm == 1; }
bool has_mag_filter() const { return data_.mag_filter != 0x3; }
TextureFilter mag_filter() const {
return static_cast<TextureFilter>(data_.mag_filter);
}
bool has_min_filter() const { return data_.min_filter != 0x3; }
TextureFilter min_filter() const {
return static_cast<TextureFilter>(data_.min_filter);
}
bool has_mip_filter() const { return data_.mip_filter != 0x3; }
TextureFilter mip_filter() const {
return static_cast<TextureFilter>(data_.mip_filter);
}
bool has_aniso_filter() const { return data_.aniso_filter != 0x7; }
AnisoFilter aniso_filter() const {
return static_cast<AnisoFilter>(data_.aniso_filter);
}
bool use_computed_lod() const { return data_.use_comp_lod == 1; }
bool use_register_lod() const { return data_.use_reg_lod == 1; }
bool use_register_gradients() const { return data_.use_reg_gradients == 1; }
SampleLocation sample_location() const {
return static_cast<SampleLocation>(data_.sample_location);
}
float lod_bias() const {
// http://web.archive.org/web/20090514012026/http://msdn.microsoft.com:80/en-us/library/bb313957.aspx
return ((static_cast<int>(data_.lod_bias) << 25) >> 25) / 16.0f;
}
float offset_x() const {
return ((static_cast<int>(data_.offset_x) << 27) >> 27) / 2.0f;
}
float offset_y() const {
return ((static_cast<int>(data_.offset_y) << 27) >> 27) / 2.0f;
}
float offset_z() const {
return ((static_cast<int>(data_.offset_z) << 27) >> 27) / 2.0f;
}
private:
XEPACKEDSTRUCT(Data, {
XEPACKEDSTRUCTANONYMOUS({
uint32_t opcode_value : 5;
uint32_t src_reg : 6;
uint32_t src_reg_am : 1;
uint32_t dst_reg : 6;
uint32_t dst_reg_am : 1;
uint32_t fetch_valid_only : 1;
uint32_t const_index : 5;
uint32_t tx_coord_denorm : 1;
uint32_t src_swiz : 6; // xyz
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t dst_swiz : 12; // xyzw
uint32_t mag_filter : 2; // instr_tex_filter_t
uint32_t min_filter : 2; // instr_tex_filter_t
uint32_t mip_filter : 2; // instr_tex_filter_t
uint32_t aniso_filter : 3; // instr_aniso_filter_t
uint32_t arbitrary_filter : 3; // instr_arbitrary_filter_t
uint32_t vol_mag_filter : 2; // instr_tex_filter_t
uint32_t vol_min_filter : 2; // instr_tex_filter_t
uint32_t use_comp_lod : 1;
uint32_t use_reg_lod : 1;
uint32_t unk : 1;
uint32_t is_predicated : 1;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t use_reg_gradients : 1;
uint32_t sample_location : 1;
uint32_t lod_bias : 7;
uint32_t unused : 5;
uint32_t dimension : 2;
uint32_t offset_x : 5;
uint32_t offset_y : 5;
uint32_t offset_z : 5;
uint32_t pred_condition : 1;
});
});
Data data_;
};
static_assert_size(TextureFetchInstruction, 12);
// What follows is largely a mash up of the microcode assembly naming and the
// R600 docs that have a near 1:1 with the instructions available in the xenos
// GPU. Some of the behavior has been experimentally verified. Some has been
// guessed.
// Docs: https://www.x.org/docs/AMD/old/r600isa.pdf
//
// Conventions:
// - All temporary registers are vec4s.
// - Scalar ops swizzle out a single component of their source registers denoted
// by 'a' or 'b'. src0.a means 'the first component specified for src0' and
// src0.ab means 'two components specified for src0, in order'.
// - Scalar ops write the result to the entire destination register.
// - pv and ps are the previous results of a vector or scalar ALU operation.
// Both are valid only within the current ALU clause. They are not modified
// when write masks are disabled or the instruction that would write them
// fails its predication check.
enum class AluScalarOpcode {
// Floating-Point Add
// adds dest, src0.ab
// dest.xyzw = src0.a + src0.b;
kAdds = 0,
// Floating-Point Add (with Previous)
// adds_prev dest, src0.a
// dest.xyzw = src0.a + ps;
kAddsPrev = 1,
// Floating-Point Multiply
// muls dest, src0.ab
// dest.xyzw = src0.a * src0.b;
kMuls = 2,
// Floating-Point Multiply (with Previous)
// muls_prev dest, src0.a
// dest.xyzw = src0.a * ps;
kMulsPrev = 3,
// Scalar Multiply Emulating LIT Operation
// muls_prev2 dest, src0.ab
// dest.xyzw =
// ps == -FLT_MAX || !isfinite(ps) || !isfinite(src0.b) || src0.b <= 0
// ? -FLT_MAX : src0.a * ps;
kMulsPrev2 = 4,
// Floating-Point Maximum
// maxs dest, src0.ab
// dest.xyzw = src0.a >= src0.b ? src0.a : src0.b;
kMaxs = 5,
// Floating-Point Minimum
// mins dest, src0.ab
// dest.xyzw = src0.a < src0.b ? src0.a : src0.b;
kMins = 6,
// Floating-Point Set If Equal
// seqs dest, src0.a
// dest.xyzw = src0.a == 0.0 ? 1.0 : 0.0;
kSeqs = 7,
// Floating-Point Set If Greater Than
// sgts dest, src0.a
// dest.xyzw = src0.a > 0.0 ? 1.0 : 0.0;
kSgts = 8,
// Floating-Point Set If Greater Than Or Equal
// sges dest, src0.a
// dest.xyzw = src0.a >= 0.0 ? 1.0 : 0.0;
kSges = 9,
// Floating-Point Set If Not Equal
// snes dest, src0.a
// dest.xyzw = src0.a != 0.0 ? 1.0 : 0.0;
kSnes = 10,
// Floating-Point Fractional
// frcs dest, src0.a
// dest.xyzw = src0.a - floor(src0.a);
kFrcs = 11,
// Floating-Point Truncate
// truncs dest, src0.a
// dest.xyzw = src0.a >= 0 ? floor(src0.a) : -floor(-src0.a);
kTruncs = 12,
// Floating-Point Floor
// floors dest, src0.a
// dest.xyzw = floor(src0.a);
kFloors = 13,
// Scalar Base-2 Exponent, IEEE
// exp dest, src0.a
// dest.xyzw = src0.a == 0.0 ? 1.0 : pow(2, src0.a);
kExp = 14,
// Scalar Base-2 Log
// logc dest, src0.a
// float t = src0.a == 1.0 ? 0.0 : log(src0.a) / log(2.0);
// dest.xyzw = t == -INF ? -FLT_MAX : t;
kLogc = 15,
// Scalar Base-2 IEEE Log
// log dest, src0.a
// dest.xyzw = src0.a == 1.0 ? 0.0 : log(src0.a) / log(2.0);
kLog = 16,
// Scalar Reciprocal, Clamp to Maximum
// rcpc dest, src0.a
// float t = src0.a == 1.0 ? 1.0 : 1.0 / src0.a;
// if (t == -INF) t = -FLT_MAX;
// else if (t == INF) t = FLT_MAX;
// dest.xyzw = t;
kRcpc = 17,
// Scalar Reciprocal, Clamp to Zero
// rcpf dest, src0.a
// float t = src0.a == 1.0 ? 1.0 : 1.0 / src0.a;
// if (t == -INF) t = -0.0;
// else if (t == INF) t = 0.0;
// dest.xyzw = t;
kRcpf = 18,
// Scalar Reciprocal, IEEE Approximation
// rcp dest, src0.a
// dest.xyzw = src0.a == 1.0 ? 1.0 : 1.0 / src0.a;
kRcp = 19,
// Scalar Reciprocal Square Root, Clamp to Maximum
// rsqc dest, src0.a
// float t = src0.a == 1.0 ? 1.0 : 1.0 / sqrt(src0.a);
// if (t == -INF) t = -FLT_MAX;
// else if (t == INF) t = FLT_MAX;
// dest.xyzw = t;
kRsqc = 20,
// Scalar Reciprocal Square Root, Clamp to Zero
// rsqc dest, src0.a
// float t = src0.a == 1.0 ? 1.0 : 1.0 / sqrt(src0.a);
// if (t == -INF) t = -0.0;
// else if (t == INF) t = 0.0;
// dest.xyzw = t;
kRsqf = 21,
// Scalar Reciprocal Square Root, IEEE Approximation
// rsq dest, src0.a
// dest.xyzw = src0.a == 1.0 ? 1.0 : 1.0 / sqrt(src0.a);
kRsq = 22,
// Floating-Point Maximum with Copy To Integer in AR
// maxas dest, src0.ab
// movas dest, src0.aa
// int result = (int)floor(src0.a + 0.5);
// a0 = clamp(result, -256, 255);
// dest.xyzw = src0.a >= src0.b ? src0.a : src0.b;
kMaxAs = 23,
// Floating-Point Maximum with Copy Truncated To Integer in AR
// maxasf dest, src0.ab
// movasf dest, src0.aa
// int result = (int)floor(src0.a);
// a0 = clamp(result, -256, 255);
// dest.xyzw = src0.a >= src0.b ? src0.a : src0.b;
kMaxAsf = 24,
// Floating-Point Subtract
// subs dest, src0.ab
// dest.xyzw = src0.a - src0.b;
kSubs = 25,
// Floating-Point Subtract (with Previous)
// subs_prev dest, src0.a
// dest.xyzw = src0.a - ps;
kSubsPrev = 26,
// Floating-Point Predicate Set If Equal
// setp_eq dest, src0.a
// if (src0.a == 0.0) {
// dest.xyzw = 0.0;
// p0 = 1;
// } else {
// dest.xyzw = 1.0;
// p0 = 0;
// }
kSetpEq = 27,
// Floating-Point Predicate Set If Not Equal
// setp_ne dest, src0.a
// if (src0.a != 0.0) {
// dest.xyzw = 0.0;
// p0 = 1;
// } else {
// dest.xyzw = 1.0;
// p0 = 0;
// }
kSetpNe = 28,
// Floating-Point Predicate Set If Greater Than
// setp_gt dest, src0.a
// if (src0.a > 0.0) {
// dest.xyzw = 0.0;
// p0 = 1;
// } else {
// dest.xyzw = 1.0;
// p0 = 0;
// }
kSetpGt = 29,
// Floating-Point Predicate Set If Greater Than Or Equal
// setp_ge dest, src0.a
// if (src0.a >= 0.0) {
// dest.xyzw = 0.0;
// p0 = 1;
// } else {
// dest.xyzw = 1.0;
// p0 = 0;
// }
kSetpGe = 30,
// Predicate Counter Invert
// setp_inv dest, src0.a
// if (src0.a == 1.0) {
// dest.xyzw = 0.0;
// p0 = 1;
// } else {
// if (src0.a == 0.0) {
// dest.xyzw = 1.0;
// } else {
// dest.xyzw = src0.a;
// }
// p0 = 0;
// }
kSetpInv = 31,
// Predicate Counter Pop
// setp_pop dest, src0.a
// if (src0.a - 1.0 <= 0.0) {
// dest.xyzw = 0.0;
// p0 = 1;
// } else {
// dest.xyzw = src0.a - 1.0;
// p0 = 0;
// }
kSetpPop = 32,
// Predicate Counter Clear
// setp_clr dest
// dest.xyzw = FLT_MAX;
// p0 = 0;
kSetpClr = 33,
// Predicate Counter Restore
// setp_rstr dest, src0.a
// if (src0.a == 0.0) {
// dest.xyzw = 0.0;
// p0 = 1;
// } else {
// dest.xyzw = src0.a;
// p0 = 0;
// }
kSetpRstr = 34,
// Floating-Point Pixel Kill If Equal
// kills_eq dest, src0.a
// if (src0.a == 0.0) {
// dest.xyzw = 1.0;
// discard;
// } else {
// dest.xyzw = 0.0;
// }
kKillsEq = 35,
// Floating-Point Pixel Kill If Greater Than
// kills_gt dest, src0.a
// if (src0.a > 0.0) {
// dest.xyzw = 1.0;
// discard;
// } else {
// dest.xyzw = 0.0;
// }
kKillsGt = 36,
// Floating-Point Pixel Kill If Greater Than Or Equal
// kills_ge dest, src0.a
// if (src0.a >= 0.0) {
// dest.xyzw = 1.0;
// discard;
// } else {
// dest.xyzw = 0.0;
// }
kKillsGe = 37,
// Floating-Point Pixel Kill If Not Equal
// kills_ne dest, src0.a
// if (src0.a != 0.0) {
// dest.xyzw = 1.0;
// discard;
// } else {
// dest.xyzw = 0.0;
// }
kKillsNe = 38,
// Floating-Point Pixel Kill If One
// kills_one dest, src0.a
// if (src0.a == 1.0) {
// dest.xyzw = 1.0;
// discard;
// } else {
// dest.xyzw = 0.0;
// }
kKillsOne = 39,
// Scalar Square Root, IEEE Aproximation
// sqrt dest, src0.a
// dest.xyzw = sqrt(src0.a);
kSqrt = 40,
// mulsc dest, src0.a, src1.a
kMulsc0 = 42,
// mulsc dest, src0.a, src1.a
kMulsc1 = 43,
// addsc dest, src0.a, src1.a
kAddsc0 = 44,
// addsc dest, src0.a, src1.a
kAddsc1 = 45,
// subsc dest, src0.a, src1.a
kSubsc0 = 46,
// subsc dest, src0.a, src1.a
kSubsc1 = 47,
// Scalar Sin
// sin dest, src0.a
// dest.xyzw = sin(src0.a);
kSin = 48,
// Scalar Cos
// cos dest, src0.a
// dest.xyzw = cos(src0.a);
kCos = 49,
// retain_prev dest
// dest.xyzw = ps;
kRetainPrev = 50,
};
enum class AluVectorOpcode {
// Per-Component Floating-Point Add
// add dest, src0, src1
// dest.x = src0.x + src1.x;
// dest.y = src0.y + src1.y;
// dest.z = src0.z + src1.z;
// dest.w = src0.w + src1.w;
kAdd = 0,
// Per-Component Floating-Point Multiply
// mul dest, src0, src1
// dest.x = src0.x * src1.x;
// dest.y = src0.y * src1.y;
// dest.z = src0.z * src1.z;
// dest.w = src0.w * src1.w;
kMul = 1,
// Per-Component Floating-Point Maximum
// max dest, src0, src1
// dest.x = src0.x >= src1.x ? src0.x : src1.x;
// dest.y = src0.x >= src1.y ? src0.y : src1.y;
// dest.z = src0.x >= src1.z ? src0.z : src1.z;
// dest.w = src0.x >= src1.w ? src0.w : src1.w;
kMax = 2,
// Per-Component Floating-Point Minimum
// min dest, src0, src1
// dest.x = src0.x < src1.x ? src0.x : src1.x;
// dest.y = src0.x < src1.y ? src0.y : src1.y;
// dest.z = src0.x < src1.z ? src0.z : src1.z;
// dest.w = src0.x < src1.w ? src0.w : src1.w;
kMin = 3,
// Per-Component Floating-Point Set If Equal
// seq dest, src0, src1
// dest.x = src0.x == src1.x ? 1.0 : 0.0;
// dest.y = src0.y == src1.y ? 1.0 : 0.0;
// dest.z = src0.z == src1.z ? 1.0 : 0.0;
// dest.w = src0.w == src1.w ? 1.0 : 0.0;
kSeq = 4,
// Per-Component Floating-Point Set If Greater Than
// sgt dest, src0, src1
// dest.x = src0.x > src1.x ? 1.0 : 0.0;
// dest.y = src0.y > src1.y ? 1.0 : 0.0;
// dest.z = src0.z > src1.z ? 1.0 : 0.0;
// dest.w = src0.w > src1.w ? 1.0 : 0.0;
kSgt = 5,
// Per-Component Floating-Point Set If Greater Than Or Equal
// sge dest, src0, src1
// dest.x = src0.x >= src1.x ? 1.0 : 0.0;
// dest.y = src0.y >= src1.y ? 1.0 : 0.0;
// dest.z = src0.z >= src1.z ? 1.0 : 0.0;
// dest.w = src0.w >= src1.w ? 1.0 : 0.0;
kSge = 6,
// Per-Component Floating-Point Set If Not Equal
// sne dest, src0, src1
// dest.x = src0.x != src1.x ? 1.0 : 0.0;
// dest.y = src0.y != src1.y ? 1.0 : 0.0;
// dest.z = src0.z != src1.z ? 1.0 : 0.0;
// dest.w = src0.w != src1.w ? 1.0 : 0.0;
kSne = 7,
// Per-Component Floating-Point Fractional
// frc dest, src0
// dest.x = src0.x - floor(src0.x);
// dest.y = src0.y - floor(src0.y);
// dest.z = src0.z - floor(src0.z);
// dest.w = src0.w - floor(src0.w);
kFrc = 8,
// Per-Component Floating-Point Truncate
// trunc dest, src0
// dest.x = src0.x >= 0 ? floor(src0.x) : -floor(-src0.x);
// dest.y = src0.y >= 0 ? floor(src0.y) : -floor(-src0.y);
// dest.z = src0.z >= 0 ? floor(src0.z) : -floor(-src0.z);
// dest.w = src0.w >= 0 ? floor(src0.w) : -floor(-src0.w);
kTrunc = 9,
// Per-Component Floating-Point Floor
// floor dest, src0
// dest.x = floor(src0.x);
// dest.y = floor(src0.y);
// dest.z = floor(src0.z);
// dest.w = floor(src0.w);
kFloor = 10,
// Per-Component Floating-Point Multiply-Add
// mad dest, src0, src1, src2
// dest.x = src0.x * src1.x + src2.x;
// dest.y = src0.y * src1.y + src2.y;
// dest.z = src0.z * src1.z + src2.z;
// dest.w = src0.w * src1.w + src2.w;
kMad = 11,
// Per-Component Floating-Point Conditional Move If Equal
// cndeq dest, src0, src1, src2
// dest.x = src0.x == 0.0 ? src1.x : src2.x;
// dest.y = src0.y == 0.0 ? src1.y : src2.y;
// dest.z = src0.z == 0.0 ? src1.z : src2.z;
// dest.w = src0.w == 0.0 ? src1.w : src2.w;
kCndEq = 12,
// Per-Component Floating-Point Conditional Move If Greater Than Or Equal
// cndge dest, src0, src1, src2
// dest.x = src0.x >= 0.0 ? src1.x : src2.x;
// dest.y = src0.y >= 0.0 ? src1.y : src2.y;
// dest.z = src0.z >= 0.0 ? src1.z : src2.z;
// dest.w = src0.w >= 0.0 ? src1.w : src2.w;
kCndGe = 13,
// Per-Component Floating-Point Conditional Move If Greater Than
// cndgt dest, src0, src1, src2
// dest.x = src0.x > 0.0 ? src1.x : src2.x;
// dest.y = src0.y > 0.0 ? src1.y : src2.y;
// dest.z = src0.z > 0.0 ? src1.z : src2.z;
// dest.w = src0.w > 0.0 ? src1.w : src2.w;
kCndGt = 14,
// Four-Element Dot Product
// dp4 dest, src0, src1
// dest.xyzw = src0.x * src1.x + src0.y * src1.y + src0.z * src1.z +
// src0.w * src1.w;
// Note: only pv.x contains the value.
kDp4 = 15,
// Three-Element Dot Product
// dp3 dest, src0, src1
// dest.xyzw = src0.x * src1.x + src0.y * src1.y + src0.z * src1.z;
// Note: only pv.x contains the value.
kDp3 = 16,
// Two-Element Dot Product and Add
// dp2add dest, src0, src1, src2
// dest.xyzw = src0.x * src1.x + src0.y * src1.y + src2.x;
// Note: only pv.x contains the value.
kDp2Add = 17,
// Cube Map
// cube dest, src0, src1
// dest.x = T cube coordinate;
// dest.y = S cube coordinate;
// dest.z = 2.0 * MajorAxis;
// dest.w = FaceID;
// Expects src0.zzxy and src1.yxzz swizzles.
// FaceID is D3DCUBEMAP_FACES:
// https://msdn.microsoft.com/en-us/library/windows/desktop/bb172528(v=vs.85).aspx
kCube = 18,
// Four-Element Maximum
// max4 dest, src0
// dest.xyzw = max(src0.x, src0.y, src0.z, src0.w);
// Note: only pv.x contains the value.
kMax4 = 19,
// Floating-Point Predicate Counter Increment If Equal
// setp_eq_push dest, src0, src1
// if (src0.w == 0.0 && src1.w == 0.0) {
// p0 = 1;
// } else {
// p0 = 0;
// }
// if (src0.x == 0.0 && src1.x == 0.0) {
// dest.xyzw = 0.0;
// } else {
// dest.xyzw = src0.x + 1.0;
// }
kSetpEqPush = 20,
// Floating-Point Predicate Counter Increment If Not Equal
// setp_ne_push dest, src0, src1
// if (src0.w == 0.0 && src1.w != 0.0) {
// p0 = 1;
// } else {
// p0 = 0;
// }
// if (src0.x == 0.0 && src1.x != 0.0) {
// dest.xyzw = 0.0;
// } else {
// dest.xyzw = src0.x + 1.0;
// }
kSetpNePush = 21,
// Floating-Point Predicate Counter Increment If Greater Than
// setp_gt_push dest, src0, src1
// if (src0.w == 0.0 && src1.w > 0.0) {
// p0 = 1;
// } else {
// p0 = 0;
// }
// if (src0.x == 0.0 && src1.x > 0.0) {
// dest.xyzw = 0.0;
// } else {
// dest.xyzw = src0.x + 1.0;
// }
kSetpGtPush = 22,
// Floating-Point Predicate Counter Increment If Greater Than Or Equal
// setp_ge_push dest, src0, src1
// if (src0.w == 0.0 && src1.w >= 0.0) {
// p0 = 1;
// } else {
// p0 = 0;
// }
// if (src0.x == 0.0 && src1.x >= 0.0) {
// dest.xyzw = 0.0;
// } else {
// dest.xyzw = src0.x + 1.0;
// }
kSetpGePush = 23,
// Floating-Point Pixel Kill If Equal
// kill_eq dest, src0, src1
// if (src0.x == src1.x ||
// src0.y == src1.y ||
// src0.z == src1.z ||
// src0.w == src1.w) {
// dest.xyzw = 1.0;
// discard;
// } else {
// dest.xyzw = 0.0;
// }
kKillEq = 24,
// Floating-Point Pixel Kill If Greater Than
// kill_gt dest, src0, src1
// if (src0.x > src1.x ||
// src0.y > src1.y ||
// src0.z > src1.z ||
// src0.w > src1.w) {
// dest.xyzw = 1.0;
// discard;
// } else {
// dest.xyzw = 0.0;
// }
kKillGt = 25,
// Floating-Point Pixel Kill If Equal
// kill_ge dest, src0, src1
// if (src0.x >= src1.x ||
// src0.y >= src1.y ||
// src0.z >= src1.z ||
// src0.w >= src1.w) {
// dest.xyzw = 1.0;
// discard;
// } else {
// dest.xyzw = 0.0;
// }
kKillGe = 26,
// Floating-Point Pixel Kill If Equal
// kill_ne dest, src0, src1
// if (src0.x != src1.x ||
// src0.y != src1.y ||
// src0.z != src1.z ||
// src0.w != src1.w) {
// dest.xyzw = 1.0;
// discard;
// } else {
// dest.xyzw = 0.0;
// }
kKillNe = 27,
// dst dest, src0, src1
// dest.x = 1.0;
// dest.y = src0.y * src1.y;
// dest.z = src0.z;
// dest.w = src1.w;
kDst = 28,
// Per-Component Floating-Point Maximum with Copy To Integer in AR
// maxa dest, src0, src1
// This is a combined max + mova.
// int result = (int)floor(src0.w + 0.5);
// a0 = clamp(result, -256, 255);
// dest.x = src0.x >= src1.x ? src0.x : src1.x;
// dest.y = src0.x >= src1.y ? src0.y : src1.y;
// dest.z = src0.x >= src1.z ? src0.z : src1.z;
// dest.w = src0.x >= src1.w ? src0.w : src1.w;
kMaxA = 29,
};
// Whether the vector instruction has side effects such as discarding a pixel or
// setting the predicate and can't be ignored even if it doesn't write to
// anywhere.
inline bool AluVectorOpcodeHasSideEffects(AluVectorOpcode vector_opcode) {
switch (vector_opcode) {
case AluVectorOpcode::kSetpEqPush:
case AluVectorOpcode::kSetpNePush:
case AluVectorOpcode::kSetpGtPush:
case AluVectorOpcode::kSetpGePush:
case AluVectorOpcode::kKillEq:
case AluVectorOpcode::kKillGt:
case AluVectorOpcode::kKillGe:
case AluVectorOpcode::kKillNe:
case AluVectorOpcode::kMaxA:
return true;
default:
break;
}
return false;
}
struct AluInstruction {
// Whether data is being exported (or written to local registers).
bool is_export() const { return data_.export_data == 1; }
bool export_write_mask() const { return data_.scalar_dest_rel == 1; }
// Whether the jump is predicated (or conditional).
bool is_predicated() const { return data_.is_predicated; }
// Required condition value of the comparision (true or false).
bool predicate_condition() const { return data_.pred_condition == 1; }
bool abs_constants() const { return data_.abs_constants == 1; }
bool is_const_0_addressed() const { return data_.const_0_rel_abs == 1; }
bool is_const_1_addressed() const { return data_.const_1_rel_abs == 1; }
bool is_address_relative() const { return data_.address_absolute == 1; }
bool has_vector_op() const {
return vector_write_mask() || is_export() ||
AluVectorOpcodeHasSideEffects(vector_opcode());
}
AluVectorOpcode vector_opcode() const {
return static_cast<AluVectorOpcode>(data_.vector_opc);
}
uint32_t vector_write_mask() const { return data_.vector_write_mask; }
uint32_t vector_dest() const { return data_.vector_dest; }
bool is_vector_dest_relative() const { return data_.vector_dest_rel == 1; }
bool vector_clamp() const { return data_.vector_clamp == 1; }
bool has_scalar_op() const {
return scalar_opcode() != AluScalarOpcode::kRetainPrev ||
(!is_export() && scalar_write_mask() != 0);
}
AluScalarOpcode scalar_opcode() const {
return static_cast<AluScalarOpcode>(data_.scalar_opc);
}
uint32_t scalar_write_mask() const { return data_.scalar_write_mask; }
uint32_t scalar_dest() const { return data_.scalar_dest; }
bool is_scalar_dest_relative() const { return data_.scalar_dest_rel == 1; }
bool scalar_clamp() const { return data_.scalar_clamp == 1; }
uint32_t src_reg(size_t i) const {
switch (i) {
case 1:
return data_.src1_reg;
case 2:
return data_.src2_reg;
case 3:
return data_.src3_reg;
default:
assert_unhandled_case(i);
return 0;
}
}
bool src_is_temp(size_t i) const {
switch (i) {
case 1:
return data_.src1_sel == 1;
case 2:
return data_.src2_sel == 1;
case 3:
return data_.src3_sel == 1;
default:
assert_unhandled_case(i);
return 0;
}
}
uint32_t src_swizzle(size_t i) const {
switch (i) {
case 1:
return data_.src1_swiz;
case 2:
return data_.src2_swiz;
case 3:
return data_.src3_swiz;
default:
assert_unhandled_case(i);
return 0;
}
}
bool src_negate(size_t i) const {
switch (i) {
case 1:
return data_.src1_reg_negate == 1;
case 2:
return data_.src2_reg_negate == 1;
case 3:
return data_.src3_reg_negate == 1;
default:
assert_unhandled_case(i);
return 0;
}
}
private:
XEPACKEDSTRUCT(Data, {
XEPACKEDSTRUCTANONYMOUS({
uint32_t vector_dest : 6;
uint32_t vector_dest_rel : 1;
uint32_t abs_constants : 1;
uint32_t scalar_dest : 6;
uint32_t scalar_dest_rel : 1;
uint32_t export_data : 1;
uint32_t vector_write_mask : 4;
uint32_t scalar_write_mask : 4;
uint32_t vector_clamp : 1;
uint32_t scalar_clamp : 1;
uint32_t scalar_opc : 6; // instr_scalar_opc_t
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t src3_swiz : 8;
uint32_t src2_swiz : 8;
uint32_t src1_swiz : 8;
uint32_t src3_reg_negate : 1;
uint32_t src2_reg_negate : 1;
uint32_t src1_reg_negate : 1;
uint32_t pred_condition : 1;
uint32_t is_predicated : 1;
uint32_t address_absolute : 1;
uint32_t const_1_rel_abs : 1;
uint32_t const_0_rel_abs : 1;
});
XEPACKEDSTRUCTANONYMOUS({
uint32_t src3_reg : 8;
uint32_t src2_reg : 8;
uint32_t src1_reg : 8;
uint32_t vector_opc : 5; // instr_vector_opc_t
uint32_t src3_sel : 1;
uint32_t src2_sel : 1;
uint32_t src1_sel : 1;
});
});
Data data_;
};
static_assert_size(AluInstruction, 12);
} // namespace ucode
} // namespace gpu
} // namespace xe
#endif // XENIA_GPU_UCODE_H_