Switching from fork to main glslang spirv builder.
This commit is contained in:
parent
00594da417
commit
35e08d9428
|
@ -7,6 +7,7 @@ project("xenia-gpu")
|
||||||
kind("StaticLib")
|
kind("StaticLib")
|
||||||
language("C++")
|
language("C++")
|
||||||
links({
|
links({
|
||||||
|
"glslang-spirv",
|
||||||
"snappy",
|
"snappy",
|
||||||
"spirv-tools",
|
"spirv-tools",
|
||||||
"xenia-base",
|
"xenia-base",
|
||||||
|
@ -29,6 +30,7 @@ project("xenia-gpu-shader-compiler")
|
||||||
language("C++")
|
language("C++")
|
||||||
links({
|
links({
|
||||||
"gflags",
|
"gflags",
|
||||||
|
"glslang-spirv",
|
||||||
"spirv-tools",
|
"spirv-tools",
|
||||||
"xenia-base",
|
"xenia-base",
|
||||||
"xenia-gpu",
|
"xenia-gpu",
|
||||||
|
|
|
@ -137,7 +137,7 @@ bool ShaderTranslator::Translate(Shader* shader) {
|
||||||
constant_register_map_.packed_byte_length +=
|
constant_register_map_.packed_byte_length +=
|
||||||
4 * xe::bit_count(constant_register_map_.int_bitmap);
|
4 * xe::bit_count(constant_register_map_.int_bitmap);
|
||||||
// Direct map between words and words we upload.
|
// Direct map between words and words we upload.
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 8; ++i) {
|
||||||
if (constant_register_map_.bool_bitmap[i]) {
|
if (constant_register_map_.bool_bitmap[i]) {
|
||||||
constant_register_map_.packed_byte_length += 4;
|
constant_register_map_.packed_byte_length += 4;
|
||||||
}
|
}
|
||||||
|
@ -161,6 +161,8 @@ bool ShaderTranslator::Translate(Shader* shader) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PostTranslation(shader);
|
||||||
|
|
||||||
return shader->is_valid_;
|
return shader->is_valid_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -71,6 +71,13 @@ class ShaderTranslator {
|
||||||
return std::vector<uint8_t>();
|
return std::vector<uint8_t>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handles post-translation tasks when the shader has been fully translated.
|
||||||
|
virtual void PostTranslation(Shader* shader) {}
|
||||||
|
// Sets the host disassembly on a shader.
|
||||||
|
void set_host_disassembly(Shader* shader, std::string value) {
|
||||||
|
shader->host_disassembly_ = std::move(value);
|
||||||
|
}
|
||||||
|
|
||||||
// Handles translation for control flow label addresses.
|
// Handles translation for control flow label addresses.
|
||||||
// This is triggered once for each label required (due to control flow
|
// This is triggered once for each label required (due to control flow
|
||||||
// operations) before any of the instructions within the target exec.
|
// operations) before any of the instructions within the target exec.
|
||||||
|
|
|
@ -11,124 +11,182 @@
|
||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "xenia/base/logging.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
|
||||||
|
using spv::GLSLstd450;
|
||||||
|
using spv::Id;
|
||||||
|
using spv::Op;
|
||||||
|
|
||||||
SpirvShaderTranslator::SpirvShaderTranslator() = default;
|
SpirvShaderTranslator::SpirvShaderTranslator() = default;
|
||||||
|
|
||||||
SpirvShaderTranslator::~SpirvShaderTranslator() = default;
|
SpirvShaderTranslator::~SpirvShaderTranslator() = default;
|
||||||
|
|
||||||
void SpirvShaderTranslator::StartTranslation() {
|
void SpirvShaderTranslator::StartTranslation() {
|
||||||
auto& e = emitter_;
|
// Create a new builder.
|
||||||
|
builder_ = std::make_unique<spv::Builder>(0xFFFFFFFF);
|
||||||
|
auto& b = *builder_;
|
||||||
|
|
||||||
auto fn = e.MakeMainEntry();
|
// Import required modules.
|
||||||
auto float_1_0 = e.MakeFloatConstant(1.0f);
|
glsl_std_450_instruction_set_ = b.import("GLSL.std.450");
|
||||||
auto acos = e.CreateGlslStd450InstructionCall(
|
|
||||||
spv::Decoration::Invariant, e.MakeFloatType(32), spv::GLSLstd450::kAcos,
|
// Configure environment.
|
||||||
{float_1_0});
|
b.setSource(spv::SourceLanguage::SourceLanguageUnknown, 0);
|
||||||
e.MakeReturn(true);
|
b.setMemoryModel(spv::AddressingModel::AddressingModelLogical,
|
||||||
|
spv::MemoryModel::MemoryModelGLSL450);
|
||||||
|
b.addCapability(spv::Capability::CapabilityShader);
|
||||||
|
b.addCapability(spv::Capability::CapabilityGenericPointer);
|
||||||
|
if (is_vertex_shader()) {
|
||||||
|
b.addCapability(spv::Capability::CapabilityClipDistance);
|
||||||
|
b.addCapability(spv::Capability::CapabilityCullDistance);
|
||||||
|
}
|
||||||
|
if (is_pixel_shader()) {
|
||||||
|
b.addCapability(spv::Capability::CapabilityDerivativeControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
// main() entry point.
|
||||||
|
auto mainFn = b.makeMain();
|
||||||
|
if (is_vertex_shader()) {
|
||||||
|
b.addEntryPoint(spv::ExecutionModel::ExecutionModelVertex, mainFn, "main");
|
||||||
|
} else {
|
||||||
|
b.addEntryPoint(spv::ExecutionModel::ExecutionModelFragment, mainFn,
|
||||||
|
"main");
|
||||||
|
b.addExecutionMode(mainFn, spv::ExecutionModeOriginUpperLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(benvanik): transform feedback.
|
||||||
|
if (false) {
|
||||||
|
b.addCapability(spv::Capability::CapabilityTransformFeedback);
|
||||||
|
b.addExecutionMode(mainFn, spv::ExecutionMode::ExecutionModeXfb);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto float_1_0 = b.makeFloatConstant(2.0f);
|
||||||
|
auto acos = CreateGlslStd450InstructionCall(
|
||||||
|
spv::Decoration::DecorationInvariant, b.makeFloatType(32),
|
||||||
|
GLSLstd450::kAcos, {float_1_0});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
std::vector<uint8_t> SpirvShaderTranslator::CompleteTranslation() {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
|
b.makeReturn(false);
|
||||||
|
|
||||||
std::vector<uint32_t> spirv_words;
|
std::vector<uint32_t> spirv_words;
|
||||||
e.Serialize(spirv_words);
|
b.dump(spirv_words);
|
||||||
|
|
||||||
|
// Cleanup builder.
|
||||||
|
builder_.reset();
|
||||||
|
|
||||||
|
// Copy bytes out.
|
||||||
|
// TODO(benvanik): avoid copy?
|
||||||
std::vector<uint8_t> spirv_bytes;
|
std::vector<uint8_t> spirv_bytes;
|
||||||
spirv_bytes.resize(spirv_words.size() * 4);
|
spirv_bytes.resize(spirv_words.size() * 4);
|
||||||
std::memcpy(spirv_bytes.data(), spirv_words.data(), spirv_bytes.size());
|
std::memcpy(spirv_bytes.data(), spirv_words.data(), spirv_bytes.size());
|
||||||
return spirv_bytes;
|
return spirv_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpirvShaderTranslator::PostTranslation(Shader* shader) {
|
||||||
|
// TODO(benvanik): only if needed? could be slowish.
|
||||||
|
auto disasm = disassembler_.Disassemble(
|
||||||
|
reinterpret_cast<const uint32_t*>(shader->translated_binary().data()),
|
||||||
|
shader->translated_binary().size() / 4);
|
||||||
|
if (disasm->has_error()) {
|
||||||
|
XELOGE("Failed to disassemble SPIRV - invalid?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
set_host_disassembly(shader, disasm->to_string());
|
||||||
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessLabel(uint32_t cf_index) {
|
void SpirvShaderTranslator::ProcessLabel(uint32_t cf_index) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessControlFlowNopInstruction() {
|
void SpirvShaderTranslator::ProcessControlFlowNopInstruction() {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessExecInstructionBegin(
|
void SpirvShaderTranslator::ProcessExecInstructionBegin(
|
||||||
const ParsedExecInstruction& instr) {
|
const ParsedExecInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessExecInstructionEnd(
|
void SpirvShaderTranslator::ProcessExecInstructionEnd(
|
||||||
const ParsedExecInstruction& instr) {
|
const ParsedExecInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessLoopStartInstruction(
|
void SpirvShaderTranslator::ProcessLoopStartInstruction(
|
||||||
const ParsedLoopStartInstruction& instr) {
|
const ParsedLoopStartInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessLoopEndInstruction(
|
void SpirvShaderTranslator::ProcessLoopEndInstruction(
|
||||||
const ParsedLoopEndInstruction& instr) {
|
const ParsedLoopEndInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessCallInstruction(
|
void SpirvShaderTranslator::ProcessCallInstruction(
|
||||||
const ParsedCallInstruction& instr) {
|
const ParsedCallInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessReturnInstruction(
|
void SpirvShaderTranslator::ProcessReturnInstruction(
|
||||||
const ParsedReturnInstruction& instr) {
|
const ParsedReturnInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessJumpInstruction(
|
void SpirvShaderTranslator::ProcessJumpInstruction(
|
||||||
const ParsedJumpInstruction& instr) {
|
const ParsedJumpInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessAllocInstruction(
|
void SpirvShaderTranslator::ProcessAllocInstruction(
|
||||||
const ParsedAllocInstruction& instr) {
|
const ParsedAllocInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessVertexFetchInstruction(
|
void SpirvShaderTranslator::ProcessVertexFetchInstruction(
|
||||||
const ParsedVertexFetchInstruction& instr) {
|
const ParsedVertexFetchInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessTextureFetchInstruction(
|
void SpirvShaderTranslator::ProcessTextureFetchInstruction(
|
||||||
const ParsedTextureFetchInstruction& instr) {
|
const ParsedTextureFetchInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessAluInstruction(
|
void SpirvShaderTranslator::ProcessAluInstruction(
|
||||||
const ParsedAluInstruction& instr) {
|
const ParsedAluInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
switch (instr.type) {
|
switch (instr.type) {
|
||||||
case ParsedAluInstruction::Type::kNop:
|
case ParsedAluInstruction::Type::kNop:
|
||||||
e.CreateNop();
|
b.createNoResultOp(spv::Op::OpNop);
|
||||||
break;
|
break;
|
||||||
case ParsedAluInstruction::Type::kVector:
|
case ParsedAluInstruction::Type::kVector:
|
||||||
ProcessVectorAluInstruction(instr);
|
ProcessVectorAluInstruction(instr);
|
||||||
|
@ -141,14 +199,14 @@ void SpirvShaderTranslator::ProcessAluInstruction(
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessVectorAluInstruction(
|
void SpirvShaderTranslator::ProcessVectorAluInstruction(
|
||||||
const ParsedAluInstruction& instr) {
|
const ParsedAluInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpirvShaderTranslator::ProcessScalarAluInstruction(
|
void SpirvShaderTranslator::ProcessScalarAluInstruction(
|
||||||
const ParsedAluInstruction& instr) {
|
const ParsedAluInstruction& instr) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
spv::Id value_id = LoadFromOperand(instr.operands[0]);
|
spv::Id value_id = LoadFromOperand(instr.operands[0]);
|
||||||
|
|
||||||
|
@ -157,11 +215,19 @@ void SpirvShaderTranslator::ProcessScalarAluInstruction(
|
||||||
EmitUnimplementedTranslationError();
|
EmitUnimplementedTranslationError();
|
||||||
}
|
}
|
||||||
|
|
||||||
spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) {
|
Id SpirvShaderTranslator::CreateGlslStd450InstructionCall(
|
||||||
auto& e = emitter_;
|
spv::Decoration precision, Id result_type, GLSLstd450 instruction_ordinal,
|
||||||
|
std::vector<Id> args) {
|
||||||
|
return builder_->createBuiltinCall(result_type, glsl_std_450_instruction_set_,
|
||||||
|
static_cast<int>(instruction_ordinal),
|
||||||
|
args);
|
||||||
|
}
|
||||||
|
|
||||||
spv::Id current_type_id = e.MakeFloatType(32);
|
spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) {
|
||||||
spv::Id current_value_id = e.CreateUndefined(current_type_id);
|
auto& b = *builder_;
|
||||||
|
|
||||||
|
spv::Id current_type_id = b.makeFloatType(32);
|
||||||
|
spv::Id current_value_id = b.createUndefined(current_type_id);
|
||||||
|
|
||||||
// storage_addressing_mode
|
// storage_addressing_mode
|
||||||
switch (op.storage_source) {
|
switch (op.storage_source) {
|
||||||
|
@ -186,13 +252,13 @@ spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (op.is_absolute_value) {
|
if (op.is_absolute_value) {
|
||||||
current_value_id = e.CreateGlslStd450InstructionCall(
|
current_value_id = CreateGlslStd450InstructionCall(
|
||||||
spv::Decoration::RelaxedPrecision, current_type_id,
|
spv::Decoration::DecorationRelaxedPrecision, current_type_id,
|
||||||
spv::GLSLstd450::kFAbs, {current_value_id});
|
GLSLstd450::kFAbs, {current_value_id});
|
||||||
}
|
}
|
||||||
if (op.is_negated) {
|
if (op.is_negated) {
|
||||||
current_value_id =
|
current_value_id =
|
||||||
e.CreateUnaryOp(spv::Op::OpFNegate, current_type_id, current_value_id);
|
b.createUnaryOp(spv::Op::OpFNegate, current_type_id, current_value_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// swizzle
|
// swizzle
|
||||||
|
@ -202,7 +268,7 @@ spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) {
|
||||||
|
|
||||||
void SpirvShaderTranslator::StoreToResult(spv::Id source_value_id,
|
void SpirvShaderTranslator::StoreToResult(spv::Id source_value_id,
|
||||||
const InstructionResult& result) {
|
const InstructionResult& result) {
|
||||||
auto& e = emitter_;
|
auto& b = *builder_;
|
||||||
|
|
||||||
if (result.storage_target == InstructionStorageTarget::kNone) {
|
if (result.storage_target == InstructionStorageTarget::kNone) {
|
||||||
// No-op?
|
// No-op?
|
||||||
|
@ -236,7 +302,7 @@ void SpirvShaderTranslator::StoreToResult(spv::Id source_value_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
spv::Id current_value_id = source_value_id;
|
spv::Id current_value_id = source_value_id;
|
||||||
spv::Id current_type_id = e.GetTypeId(source_value_id);
|
spv::Id current_type_id = b.getTypeId(source_value_id);
|
||||||
|
|
||||||
// Clamp the input value.
|
// Clamp the input value.
|
||||||
if (result.is_clamped) {
|
if (result.is_clamped) {
|
||||||
|
@ -248,7 +314,7 @@ void SpirvShaderTranslator::StoreToResult(spv::Id source_value_id,
|
||||||
// swizzle
|
// swizzle
|
||||||
|
|
||||||
// Convert to the appropriate type, if needed.
|
// Convert to the appropriate type, if needed.
|
||||||
spv::Id desired_type_id = e.MakeFloatType(32);
|
spv::Id desired_type_id = b.makeFloatType(32);
|
||||||
if (current_value_id != desired_type_id) {
|
if (current_value_id != desired_type_id) {
|
||||||
EmitTranslationError("Type conversion on storage not yet implemented");
|
EmitTranslationError("Type conversion on storage not yet implemented");
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,8 +14,10 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "third_party/glslang-spirv/SpvBuilder.h"
|
||||||
|
#include "third_party/spirv/GLSL.std.450.hpp11"
|
||||||
#include "xenia/gpu/shader_translator.h"
|
#include "xenia/gpu/shader_translator.h"
|
||||||
#include "xenia/ui/spirv/spirv_emitter.h"
|
#include "xenia/ui/spirv/spirv_disassembler.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace gpu {
|
namespace gpu {
|
||||||
|
@ -28,6 +30,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||||
protected:
|
protected:
|
||||||
void StartTranslation() override;
|
void StartTranslation() override;
|
||||||
std::vector<uint8_t> CompleteTranslation() override;
|
std::vector<uint8_t> CompleteTranslation() override;
|
||||||
|
void PostTranslation(Shader* shader) override;
|
||||||
|
|
||||||
void ProcessLabel(uint32_t cf_index) override;
|
void ProcessLabel(uint32_t cf_index) override;
|
||||||
void ProcessControlFlowNopInstruction() override;
|
void ProcessControlFlowNopInstruction() override;
|
||||||
|
@ -48,9 +51,16 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||||
void ProcessAluInstruction(const ParsedAluInstruction& instr) override;
|
void ProcessAluInstruction(const ParsedAluInstruction& instr) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void SetupPushConstants();
|
||||||
|
|
||||||
void ProcessVectorAluInstruction(const ParsedAluInstruction& instr);
|
void ProcessVectorAluInstruction(const ParsedAluInstruction& instr);
|
||||||
void ProcessScalarAluInstruction(const ParsedAluInstruction& instr);
|
void ProcessScalarAluInstruction(const ParsedAluInstruction& instr);
|
||||||
|
|
||||||
|
// Creates a call to the given GLSL intrinsic.
|
||||||
|
spv::Id SpirvShaderTranslator::CreateGlslStd450InstructionCall(
|
||||||
|
spv::Decoration precision, spv::Id result_type,
|
||||||
|
spv::GLSLstd450 instruction_ordinal, std::vector<spv::Id> args);
|
||||||
|
|
||||||
// Loads an operand into a value.
|
// Loads an operand into a value.
|
||||||
// The value returned will be in the form described in the operand (number of
|
// The value returned will be in the form described in the operand (number of
|
||||||
// components, etc).
|
// components, etc).
|
||||||
|
@ -60,7 +70,11 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
||||||
// the proper components will be selected.
|
// the proper components will be selected.
|
||||||
void StoreToResult(spv::Id source_value_id, const InstructionResult& result);
|
void StoreToResult(spv::Id source_value_id, const InstructionResult& result);
|
||||||
|
|
||||||
xe::ui::spirv::SpirvEmitter emitter_;
|
xe::ui::spirv::SpirvDisassembler disassembler_;
|
||||||
|
|
||||||
|
// TODO(benvanik): replace with something better, make reusable, etc.
|
||||||
|
std::unique_ptr<spv::Builder> builder_;
|
||||||
|
spv::Id glsl_std_450_instruction_set_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace gpu
|
} // namespace gpu
|
||||||
|
|
|
@ -22,6 +22,8 @@ VulkanShader::VulkanShader(ShaderType shader_type, uint64_t data_hash,
|
||||||
|
|
||||||
VulkanShader::~VulkanShader() = default;
|
VulkanShader::~VulkanShader() = default;
|
||||||
|
|
||||||
|
bool VulkanShader::Prepare() { return true; }
|
||||||
|
|
||||||
} // namespace vulkan
|
} // namespace vulkan
|
||||||
} // namespace gpu
|
} // namespace gpu
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -24,6 +24,13 @@ class VulkanShader : public Shader {
|
||||||
VulkanShader(ShaderType shader_type, uint64_t data_hash,
|
VulkanShader(ShaderType shader_type, uint64_t data_hash,
|
||||||
const uint32_t* dword_ptr, uint32_t dword_count);
|
const uint32_t* dword_ptr, uint32_t dword_count);
|
||||||
~VulkanShader() override;
|
~VulkanShader() override;
|
||||||
|
|
||||||
|
VkShaderModule shader_module() const { return shader_module_; }
|
||||||
|
|
||||||
|
bool Prepare();
|
||||||
|
|
||||||
|
private:
|
||||||
|
VkShaderModule shader_module_ = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace vulkan
|
} // namespace vulkan
|
||||||
|
|
|
@ -7,6 +7,7 @@ project("xenia-ui-spirv")
|
||||||
kind("StaticLib")
|
kind("StaticLib")
|
||||||
language("C++")
|
language("C++")
|
||||||
links({
|
links({
|
||||||
|
"glslang-spirv",
|
||||||
"spirv-tools",
|
"spirv-tools",
|
||||||
"xenia-base",
|
"xenia-base",
|
||||||
})
|
})
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,731 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* 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. *
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Contents originally forked from:
|
|
||||||
// https://github.com/KhronosGroup/glslang/
|
|
||||||
//
|
|
||||||
// Copyright (C) 2014 LunarG, Inc.
|
|
||||||
//
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions
|
|
||||||
// are met:
|
|
||||||
//
|
|
||||||
// Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
//
|
|
||||||
// Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following
|
|
||||||
// disclaimer in the documentation and/or other materials provided
|
|
||||||
// with the distribution.
|
|
||||||
//
|
|
||||||
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived
|
|
||||||
// from this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
||||||
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
||||||
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
||||||
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
#ifndef XENIA_UI_SPIRV_SPIRV_EMITTER_H_
|
|
||||||
#define XENIA_UI_SPIRV_SPIRV_EMITTER_H_
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <map>
|
|
||||||
#include <stack>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "xenia/base/assert.h"
|
|
||||||
#include "xenia/ui/spirv/spirv_ir.h"
|
|
||||||
#include "xenia/ui/spirv/spirv_util.h"
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace ui {
|
|
||||||
namespace spirv {
|
|
||||||
|
|
||||||
class SpirvEmitter {
|
|
||||||
public:
|
|
||||||
SpirvEmitter();
|
|
||||||
~SpirvEmitter();
|
|
||||||
|
|
||||||
// Document what source language and text this module was translated from.
|
|
||||||
void SetSourceLanguage(spv::SourceLanguage language, int version) {
|
|
||||||
source_language_ = language;
|
|
||||||
source_version_ = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Document an extension to the source language. Informational only.
|
|
||||||
void AddSourceExtension(const char* ext) {
|
|
||||||
source_extensions_.push_back(ext);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set addressing model and memory model for the entire module.
|
|
||||||
void SetMemoryModel(spv::AddressingModel addressing_model,
|
|
||||||
spv::MemoryModel memory_model) {
|
|
||||||
addressing_model_ = addressing_model;
|
|
||||||
memory_model_ = memory_model;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Declare a capability used by this module.
|
|
||||||
void DeclareCapability(spv::Capability cap) { capabilities_.push_back(cap); }
|
|
||||||
|
|
||||||
// Import an extended set of instructions that can be later referenced by the
|
|
||||||
// returned id.
|
|
||||||
Id ImportExtendedInstructions(const char* name);
|
|
||||||
|
|
||||||
// For creating new types (will return old type if the requested one was
|
|
||||||
// already made).
|
|
||||||
Id MakeVoidType();
|
|
||||||
Id MakeBoolType();
|
|
||||||
Id MakePointer(spv::StorageClass storage_class, Id pointee);
|
|
||||||
Id MakeIntegerType(int bit_width, bool is_signed);
|
|
||||||
Id MakeIntType(int bit_width) { return MakeIntegerType(bit_width, true); }
|
|
||||||
Id MakeUintType(int bit_width) { return MakeIntegerType(bit_width, false); }
|
|
||||||
Id MakeFloatType(int bit_width);
|
|
||||||
Id MakeStructType(std::initializer_list<Id> members, const char* name);
|
|
||||||
Id MakePairStructType(Id type0, Id type1);
|
|
||||||
Id MakeVectorType(Id component_type, int component_count);
|
|
||||||
Id MakeMatrix2DType(Id component_type, int cols, int rows);
|
|
||||||
Id MakeArrayType(Id element_type, int length);
|
|
||||||
Id MakeRuntimeArray(Id element_type);
|
|
||||||
Id MakeFunctionType(Id return_type, std::initializer_list<Id> param_types);
|
|
||||||
Id MakeImageType(Id sampled_type, spv::Dim dim, bool has_depth,
|
|
||||||
bool is_arrayed, bool is_multisampled, int sampled,
|
|
||||||
spv::ImageFormat format);
|
|
||||||
Id MakeSamplerType();
|
|
||||||
Id MakeSampledImageType(Id image_type);
|
|
||||||
|
|
||||||
// For querying about types.
|
|
||||||
Id GetTypeId(Id result_id) const { return module_.type_id(result_id); }
|
|
||||||
Id GetDerefTypeId(Id result_id) const;
|
|
||||||
Op GetOpcode(Id id) const { return module_.instruction(id)->opcode(); }
|
|
||||||
Op GetTypeClass(Id type_id) const { return GetOpcode(type_id); }
|
|
||||||
Op GetMostBasicTypeClass(Id type_id) const;
|
|
||||||
int GetComponentCount(Id result_id) const {
|
|
||||||
return GetTypeComponentCount(GetTypeId(result_id));
|
|
||||||
}
|
|
||||||
int GetTypeComponentCount(Id type_id) const;
|
|
||||||
Id GetScalarTypeId(Id type_id) const;
|
|
||||||
Id GetContainedTypeId(Id type_id) const;
|
|
||||||
Id GetContainedTypeId(Id type_id, int member) const;
|
|
||||||
spv::StorageClass GetTypeStorageClass(Id type_id) const {
|
|
||||||
return module_.storage_class(type_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsPointer(Id result_id) const {
|
|
||||||
return IsPointerType(GetTypeId(result_id));
|
|
||||||
}
|
|
||||||
bool IsScalar(Id result_id) const {
|
|
||||||
return IsScalarType(GetTypeId(result_id));
|
|
||||||
}
|
|
||||||
bool IsVector(Id result_id) const {
|
|
||||||
return IsVectorType(GetTypeId(result_id));
|
|
||||||
}
|
|
||||||
bool IsMatrix(Id result_id) const {
|
|
||||||
return IsMatrixType(GetTypeId(result_id));
|
|
||||||
}
|
|
||||||
bool IsAggregate(Id result_id) const {
|
|
||||||
return IsAggregateType(GetTypeId(result_id));
|
|
||||||
}
|
|
||||||
bool IsBoolType(Id type_id) const {
|
|
||||||
return grouped_types_[static_cast<int>(spv::Op::OpTypeBool)].size() > 0 &&
|
|
||||||
type_id ==
|
|
||||||
grouped_types_[static_cast<int>(spv::Op::OpTypeBool)]
|
|
||||||
.back()
|
|
||||||
->result_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsPointerType(Id type_id) const {
|
|
||||||
return GetTypeClass(type_id) == spv::Op::OpTypePointer;
|
|
||||||
}
|
|
||||||
bool IsScalarType(Id type_id) const {
|
|
||||||
return GetTypeClass(type_id) == spv::Op::OpTypeFloat ||
|
|
||||||
GetTypeClass(type_id) == spv::Op::OpTypeInt ||
|
|
||||||
GetTypeClass(type_id) == spv::Op::OpTypeBool;
|
|
||||||
}
|
|
||||||
bool IsVectorType(Id type_id) const {
|
|
||||||
return GetTypeClass(type_id) == spv::Op::OpTypeVector;
|
|
||||||
}
|
|
||||||
bool IsMatrixType(Id type_id) const {
|
|
||||||
return GetTypeClass(type_id) == spv::Op::OpTypeMatrix;
|
|
||||||
}
|
|
||||||
bool IsStructType(Id type_id) const {
|
|
||||||
return GetTypeClass(type_id) == spv::Op::OpTypeStruct;
|
|
||||||
}
|
|
||||||
bool IsArrayType(Id type_id) const {
|
|
||||||
return GetTypeClass(type_id) == spv::Op::OpTypeArray;
|
|
||||||
}
|
|
||||||
bool IsAggregateType(Id type_id) const {
|
|
||||||
return IsArrayType(type_id) || IsStructType(type_id);
|
|
||||||
}
|
|
||||||
bool IsImageType(Id type_id) const {
|
|
||||||
return GetTypeClass(type_id) == spv::Op::OpTypeImage;
|
|
||||||
}
|
|
||||||
bool IsSamplerType(Id type_id) const {
|
|
||||||
return GetTypeClass(type_id) == spv::Op::OpTypeSampler;
|
|
||||||
}
|
|
||||||
bool IsSampledImageType(Id type_id) const {
|
|
||||||
return GetTypeClass(type_id) == spv::Op::OpTypeSampledImage;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsConstantOpCode(Op opcode) const;
|
|
||||||
bool IsConstant(Id result_id) const {
|
|
||||||
return IsConstantOpCode(GetOpcode(result_id));
|
|
||||||
}
|
|
||||||
bool IsConstantScalar(Id result_id) const {
|
|
||||||
return GetOpcode(result_id) == spv::Op::OpConstant;
|
|
||||||
}
|
|
||||||
uint32_t GetConstantScalar(Id result_id) const {
|
|
||||||
return module_.instruction(result_id)->immediate_operand(0);
|
|
||||||
}
|
|
||||||
spv::StorageClass GetStorageClass(Id result_id) const {
|
|
||||||
return GetTypeStorageClass(GetTypeId(result_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
int GetTypeColumnCount(Id type_id) const {
|
|
||||||
assert(IsMatrixType(type_id));
|
|
||||||
return GetTypeComponentCount(type_id);
|
|
||||||
}
|
|
||||||
int GetColumnCount(Id result_id) const {
|
|
||||||
return GetTypeColumnCount(GetTypeId(result_id));
|
|
||||||
}
|
|
||||||
int GetTypeRowCount(Id type_id) const {
|
|
||||||
assert(IsMatrixType(type_id));
|
|
||||||
return GetTypeComponentCount(GetContainedTypeId(type_id));
|
|
||||||
}
|
|
||||||
int GetRowCount(Id result_id) const {
|
|
||||||
return GetTypeRowCount(GetTypeId(result_id));
|
|
||||||
}
|
|
||||||
|
|
||||||
spv::Dim GetTypeDimensionality(Id type_id) const {
|
|
||||||
assert(IsImageType(type_id));
|
|
||||||
return static_cast<spv::Dim>(
|
|
||||||
module_.instruction(type_id)->immediate_operand(1));
|
|
||||||
}
|
|
||||||
Id GetImageType(Id result_id) const {
|
|
||||||
Id type_id = GetTypeId(result_id);
|
|
||||||
assert(IsImageType(type_id) || IsSampledImageType(type_id));
|
|
||||||
return IsSampledImageType(type_id)
|
|
||||||
? module_.instruction(type_id)->id_operand(0)
|
|
||||||
: type_id;
|
|
||||||
}
|
|
||||||
bool IsArrayedImageType(Id type_id) const {
|
|
||||||
assert(IsImageType(type_id));
|
|
||||||
return module_.instruction(type_id)->immediate_operand(3) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For making new constants (will return old constant if the requested one was
|
|
||||||
// already made).
|
|
||||||
Id MakeBoolConstant(bool value, bool is_spec_constant = false);
|
|
||||||
Id MakeIntConstant(int value, bool is_spec_constant = false) {
|
|
||||||
return MakeIntegerConstant(MakeIntType(32), static_cast<uint32_t>(value),
|
|
||||||
is_spec_constant);
|
|
||||||
}
|
|
||||||
Id MakeUintConstant(uint32_t value, bool is_spec_constant = false) {
|
|
||||||
return MakeIntegerConstant(MakeUintType(32), value, is_spec_constant);
|
|
||||||
}
|
|
||||||
template <typename T>
|
|
||||||
Id MakeUintConstant(T value, bool is_spec_constant = false) {
|
|
||||||
static_assert(sizeof(T) == sizeof(uint32_t), "Invalid type");
|
|
||||||
return MakeIntegerConstant(MakeUintType(32), static_cast<uint32_t>(value),
|
|
||||||
is_spec_constant);
|
|
||||||
}
|
|
||||||
Id MakeFloatConstant(float value, bool is_spec_constant = false);
|
|
||||||
Id MakeDoubleConstant(double value, bool is_spec_constant = false);
|
|
||||||
|
|
||||||
// Turns the array of constants into a proper constant of the requested type.
|
|
||||||
Id MakeCompositeConstant(Id type, std::initializer_list<Id> components);
|
|
||||||
|
|
||||||
// Declares an entry point and its execution model.
|
|
||||||
Instruction* AddEntryPoint(spv::ExecutionModel execution_model,
|
|
||||||
Function* entry_point, const char* name);
|
|
||||||
void AddExecutionMode(Function* entry_point,
|
|
||||||
spv::ExecutionMode execution_mode, int value1 = -1,
|
|
||||||
int value2 = -1, int value3 = -1);
|
|
||||||
void AddName(Id target_id, const char* name);
|
|
||||||
void AddMemberName(Id target_id, int member, const char* name);
|
|
||||||
void AddLine(Id target_id, Id file_name, int line_number, int column_number);
|
|
||||||
void AddDecoration(Id target_id, spv::Decoration decoration, int num = -1);
|
|
||||||
void AddMemberDecoration(Id target_id, int member, spv::Decoration,
|
|
||||||
int num = -1);
|
|
||||||
|
|
||||||
// At the end of what block do the next create*() instructions go?
|
|
||||||
Block* build_point() const { return build_point_; }
|
|
||||||
void set_build_point(Block* build_point) { build_point_ = build_point; }
|
|
||||||
|
|
||||||
// Makes the main function.
|
|
||||||
Function* MakeMainEntry();
|
|
||||||
|
|
||||||
// Makes a shader-style function, and create its entry block if entry is
|
|
||||||
// non-zero.
|
|
||||||
// Return the function, pass back the entry.
|
|
||||||
Function* MakeFunctionEntry(Id return_type, const char* name,
|
|
||||||
std::initializer_list<Id> param_types,
|
|
||||||
Block** entry = 0);
|
|
||||||
|
|
||||||
// Creates a return statement.
|
|
||||||
// An 'implicit' return is one not appearing in the source code. In the case
|
|
||||||
// of an implicit return, no post-return block is inserted.
|
|
||||||
void MakeReturn(bool implicit, Id return_value = 0);
|
|
||||||
|
|
||||||
// Generates all the code needed to finish up a function.
|
|
||||||
void LeaveFunction();
|
|
||||||
|
|
||||||
// Creates a fragment-shader discard (kill).
|
|
||||||
void MakeDiscard();
|
|
||||||
|
|
||||||
// Creates a global or function local or IO variable.
|
|
||||||
Id CreateVariable(spv::StorageClass storage_class, Id type,
|
|
||||||
const char* name = 0);
|
|
||||||
|
|
||||||
// Creates an intermediate object whose value is undefined.
|
|
||||||
Id CreateUndefined(Id type);
|
|
||||||
|
|
||||||
// Stores the given value into the specified pointer.
|
|
||||||
void CreateStore(Id pointer_id, Id value_id);
|
|
||||||
|
|
||||||
// Loads the value from the given pointer.
|
|
||||||
Id CreateLoad(Id pointer_id);
|
|
||||||
|
|
||||||
// Creates a pointer into a composite object that can be used with OpLoad and
|
|
||||||
// OpStore.
|
|
||||||
Id CreateAccessChain(spv::StorageClass storage_class, Id base_id,
|
|
||||||
std::vector<Id> index_ids);
|
|
||||||
|
|
||||||
// Queries the length of a run-time array.
|
|
||||||
Id CreateArrayLength(Id struct_id, int array_member);
|
|
||||||
|
|
||||||
Id CreateCompositeExtract(Id composite, Id type_id, uint32_t index);
|
|
||||||
Id CreateCompositeExtract(Id composite, Id type_id,
|
|
||||||
std::vector<uint32_t> indexes);
|
|
||||||
Id CreateCompositeInsert(Id object, Id composite, Id type_id, uint32_t index);
|
|
||||||
Id CreateCompositeInsert(Id object, Id composite, Id type_id,
|
|
||||||
std::vector<uint32_t> indexes);
|
|
||||||
|
|
||||||
Id CreateVectorExtractDynamic(Id vector, Id type_id, Id component_index);
|
|
||||||
Id CreateVectorInsertDynamic(Id vector, Id type_id, Id component,
|
|
||||||
Id component_index);
|
|
||||||
|
|
||||||
// Does nothing.
|
|
||||||
void CreateNop();
|
|
||||||
|
|
||||||
// Waits for other invocations of this module to reach the current point of
|
|
||||||
// execution.
|
|
||||||
void CreateControlBarrier(spv::Scope execution_scope, spv::Scope memory_scope,
|
|
||||||
spv::MemorySemanticsMask memory_semantics);
|
|
||||||
// Controls the order that memory accesses are observed.
|
|
||||||
void CreateMemoryBarrier(spv::Scope execution_scope,
|
|
||||||
spv::MemorySemanticsMask memory_semantics);
|
|
||||||
|
|
||||||
Id CreateUnaryOp(Op opcode, Id type_id, Id operand);
|
|
||||||
Id CreateBinOp(Op opcode, Id type_id, Id operand1, Id operand2);
|
|
||||||
Id CreateTriOp(Op opcode, Id type_id, Id operand1, Id operand2, Id operand3);
|
|
||||||
Id CreateOp(Op opcode, Id type_id, const std::vector<Id>& operands);
|
|
||||||
Id CreateFunctionCall(Function* function, std::vector<spv::Id> args);
|
|
||||||
|
|
||||||
// Takes an rvalue (source) and a set of channels to extract from it to
|
|
||||||
// make a new rvalue.
|
|
||||||
Id CreateSwizzle(Id type_id, Id source, std::vector<uint32_t> channels);
|
|
||||||
|
|
||||||
// Takes a copy of an lvalue (target) and a source of components, and sets the
|
|
||||||
// source components into the lvalue where the 'channels' say to put them.
|
|
||||||
Id CreateLvalueSwizzle(Id type_id, Id target, Id source,
|
|
||||||
std::vector<uint32_t> channels);
|
|
||||||
|
|
||||||
// If the value passed in is an instruction and the precision is not EMpNone,
|
|
||||||
// it gets tagged with the requested precision.
|
|
||||||
void SetPrecision(Id value, spv::Decoration precision) {
|
|
||||||
CheckNotImplemented("setPrecision");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Smears a scalar to a vector for the following forms:
|
|
||||||
// - PromoteScalar(scalar, vector) // smear scalar to width of vector
|
|
||||||
// - PromoteScalar(vector, scalar) // smear scalar to width of vector
|
|
||||||
// - PromoteScalar(pointer, scalar) // smear scalar to width of what pointer
|
|
||||||
// points to
|
|
||||||
// - PromoteScalar(scalar, scalar) // do nothing
|
|
||||||
// Other forms are not allowed.
|
|
||||||
//
|
|
||||||
// Note: One of the arguments will change, with the result coming back that
|
|
||||||
// way rather than through the return value.
|
|
||||||
void PromoteScalar(spv::Decoration precision, Id& left, Id& right);
|
|
||||||
|
|
||||||
// Makes a value by smearing the scalar to fill the type.
|
|
||||||
Id SmearScalar(spv::Decoration precision, Id scalar_value, Id vector_type_id);
|
|
||||||
|
|
||||||
// Executes an instruction in an imported set of extended instructions.
|
|
||||||
Id CreateExtendedInstructionCall(spv::Decoration precision, Id result_type,
|
|
||||||
Id instruction_set, int instruction_ordinal,
|
|
||||||
std::initializer_list<Id> args);
|
|
||||||
// Executes an instruction from the extended GLSL set.
|
|
||||||
Id CreateGlslStd450InstructionCall(spv::Decoration precision, Id result_type,
|
|
||||||
spv::GLSLstd450 instruction_ordinal,
|
|
||||||
std::initializer_list<Id> args);
|
|
||||||
|
|
||||||
// List of parameters used to create a texture operation
|
|
||||||
struct TextureParameters {
|
|
||||||
Id sampler;
|
|
||||||
Id coords;
|
|
||||||
Id bias;
|
|
||||||
Id lod;
|
|
||||||
Id depth_ref;
|
|
||||||
Id offset;
|
|
||||||
Id offsets;
|
|
||||||
Id grad_x;
|
|
||||||
Id grad_y;
|
|
||||||
Id sample;
|
|
||||||
Id comp;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Selects the correct texture operation based on all inputs, and emit the
|
|
||||||
// correct instruction.
|
|
||||||
Id CreateTextureCall(spv::Decoration precision, Id result_type, bool fetch,
|
|
||||||
bool proj, bool gather,
|
|
||||||
const TextureParameters& parameters);
|
|
||||||
|
|
||||||
// Emits the OpTextureQuery* instruction that was passed in and figures out
|
|
||||||
// the right return value and type.
|
|
||||||
Id CreateTextureQueryCall(Op opcode, const TextureParameters& parameters);
|
|
||||||
|
|
||||||
Id CreateSamplePositionCall(spv::Decoration precision, Id, Id);
|
|
||||||
Id CreateBitFieldExtractCall(spv::Decoration precision, Id, Id, Id,
|
|
||||||
bool isSigned);
|
|
||||||
Id CreateBitFieldInsertCall(spv::Decoration precision, Id, Id, Id, Id);
|
|
||||||
|
|
||||||
// Reduction comparision for composites: For equal and not-equal resulting in
|
|
||||||
// a scalar.
|
|
||||||
Id CreateCompare(spv::Decoration precision, Id value1, Id value2,
|
|
||||||
bool is_equal);
|
|
||||||
|
|
||||||
// OpCompositeConstruct
|
|
||||||
Id CreateCompositeConstruct(Id type_id, std::vector<Id> constituent_ids);
|
|
||||||
|
|
||||||
// vector or scalar constructor
|
|
||||||
Id CreateConstructor(spv::Decoration precision, std::vector<Id> source_ids,
|
|
||||||
Id result_type_id);
|
|
||||||
|
|
||||||
// matrix constructor
|
|
||||||
Id CreateMatrixConstructor(spv::Decoration precision, std::vector<Id> sources,
|
|
||||||
Id constructee);
|
|
||||||
|
|
||||||
// Helper to use for building nested control flow with if-then-else.
|
|
||||||
class If {
|
|
||||||
public:
|
|
||||||
If(SpirvEmitter& emitter, Id condition);
|
|
||||||
~If() = default;
|
|
||||||
|
|
||||||
void MakeBeginElse();
|
|
||||||
void MakeEndIf();
|
|
||||||
|
|
||||||
private:
|
|
||||||
If(const If&) = delete;
|
|
||||||
If& operator=(If&) = delete;
|
|
||||||
|
|
||||||
SpirvEmitter& emitter_;
|
|
||||||
Id condition_;
|
|
||||||
Function* function_ = nullptr;
|
|
||||||
Block* header_block_ = nullptr;
|
|
||||||
Block* then_block_ = nullptr;
|
|
||||||
Block* else_block_ = nullptr;
|
|
||||||
Block* merge_block_ = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Makes a switch statement.
|
|
||||||
// A switch has 'numSegments' of pieces of code, not containing any
|
|
||||||
// case/default labels, all separated by one or more case/default labels.
|
|
||||||
// Each possible case value v is a jump to the caseValues[v] segment. The
|
|
||||||
// defaultSegment is also in this number space. How to compute the value is
|
|
||||||
// given by 'condition', as in switch(condition).
|
|
||||||
//
|
|
||||||
// The SPIR-V Builder will maintain the stack of post-switch merge blocks for
|
|
||||||
// nested switches.
|
|
||||||
//
|
|
||||||
// Use a defaultSegment < 0 if there is no default segment (to branch to post
|
|
||||||
// switch).
|
|
||||||
//
|
|
||||||
// Returns the right set of basic blocks to start each code segment with, so
|
|
||||||
// that the caller's recursion stack can hold the memory for it.
|
|
||||||
void MakeSwitch(Id condition, int segment_count, std::vector<int> case_values,
|
|
||||||
std::vector<int> value_index_to_segment, int default_segment,
|
|
||||||
std::vector<Block*>& segment_blocks);
|
|
||||||
|
|
||||||
// Adds a branch to the innermost switch's merge block.
|
|
||||||
void AddSwitchBreak();
|
|
||||||
|
|
||||||
// Move sto the next code segment, passing in the return argument in
|
|
||||||
// MakeSwitch().
|
|
||||||
void NextSwitchSegment(std::vector<Block*>& segment_block, int next_segment);
|
|
||||||
|
|
||||||
// Finishes off the innermost switch.
|
|
||||||
void EndSwitch(std::vector<Block*>& segment_block);
|
|
||||||
|
|
||||||
// Starts the beginning of a new loop, and prepare the builder to
|
|
||||||
// generate code for the loop test.
|
|
||||||
// The test_first parameter is true when the loop test executes before
|
|
||||||
// the body (it is false for do-while loops).
|
|
||||||
void MakeNewLoop(bool test_first);
|
|
||||||
|
|
||||||
// Adds the branch for the loop test, based on the given condition.
|
|
||||||
// The true branch goes to the first block in the loop body, and
|
|
||||||
// the false branch goes to the loop's merge block. The builder insertion
|
|
||||||
// point will be placed at the start of the body.
|
|
||||||
void CreateLoopTestBranch(Id condition);
|
|
||||||
|
|
||||||
// Generates an unconditional branch to the loop body.
|
|
||||||
// The builder insertion point will be placed at the start of the body.
|
|
||||||
// Use this when there is no loop test.
|
|
||||||
void CreateBranchToBody();
|
|
||||||
|
|
||||||
// Adds a branch to the test of the current (innermost) loop.
|
|
||||||
// The way we generate code, that's also the loop header.
|
|
||||||
void CreateLoopContinue();
|
|
||||||
|
|
||||||
// Adds an exit (e.g. "break") for the innermost loop that you're in.
|
|
||||||
void CreateLoopExit();
|
|
||||||
|
|
||||||
// Close the innermost loop that you're in.
|
|
||||||
void CloseLoop();
|
|
||||||
|
|
||||||
// Access chain design for an R-Value vs. L-Value:
|
|
||||||
//
|
|
||||||
// There is a single access chain the builder is building at
|
|
||||||
// any particular time. Such a chain can be used to either to a load or
|
|
||||||
// a store, when desired.
|
|
||||||
//
|
|
||||||
// Expressions can be r-values, l-values, or both, or only r-values:
|
|
||||||
// a[b.c].d = .... // l-value
|
|
||||||
// ... = a[b.c].d; // r-value, that also looks like an l-value
|
|
||||||
// ++a[b.c].d; // r-value and l-value
|
|
||||||
// (x + y)[2]; // r-value only, can't possibly be l-value
|
|
||||||
//
|
|
||||||
// Computing an r-value means generating code. Hence,
|
|
||||||
// r-values should only be computed when they are needed, not speculatively.
|
|
||||||
//
|
|
||||||
// Computing an l-value means saving away information for later use in the
|
|
||||||
// compiler,
|
|
||||||
// no code is generated until the l-value is later dereferenced. It is okay
|
|
||||||
// to speculatively generate an l-value, just not okay to speculatively
|
|
||||||
// dereference it.
|
|
||||||
//
|
|
||||||
// The base of the access chain (the left-most variable or expression
|
|
||||||
// from which everything is based) can be set either as an l-value
|
|
||||||
// or as an r-value. Most efficient would be to set an l-value if one
|
|
||||||
// is available. If an expression was evaluated, the resulting r-value
|
|
||||||
// can be set as the chain base.
|
|
||||||
//
|
|
||||||
// The users of this single access chain can save and restore if they
|
|
||||||
// want to nest or manage multiple chains.
|
|
||||||
//
|
|
||||||
struct AccessChain {
|
|
||||||
Id base; // for l-values, pointer to the base object, for r-values, the
|
|
||||||
// base object
|
|
||||||
std::vector<Id> index_chain;
|
|
||||||
Id instr; // cache the instruction that generates this access chain
|
|
||||||
std::vector<uint32_t> swizzle; // each std::vector element selects the next
|
|
||||||
// GLSL component number
|
|
||||||
Id component; // a dynamic component index, can coexist with a swizzle,
|
|
||||||
// done after the swizzle, NoResult if not present
|
|
||||||
Id pre_swizzle_base_type; // dereferenced type, before swizzle or component
|
|
||||||
// is
|
|
||||||
// applied; NoType unless a swizzle or component is
|
|
||||||
// present
|
|
||||||
bool is_rvalue; // true if 'base' is an r-value, otherwise, base is an
|
|
||||||
// l-value
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// the SPIR-V builder maintains a single active chain that
|
|
||||||
// the following methods operated on
|
|
||||||
//
|
|
||||||
|
|
||||||
// for external save and restore
|
|
||||||
AccessChain access_chain() { return access_chain_; }
|
|
||||||
void set_access_chain(AccessChain new_chain) { access_chain_ = new_chain; }
|
|
||||||
|
|
||||||
void ClearAccessChain();
|
|
||||||
|
|
||||||
// set new base as an l-value base
|
|
||||||
void set_access_chain_lvalue(Id lvalue) {
|
|
||||||
assert(IsPointer(lvalue));
|
|
||||||
access_chain_.base = lvalue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set new base value as an r-value
|
|
||||||
void set_access_chain_rvalue(Id rvalue) {
|
|
||||||
access_chain_.is_rvalue = true;
|
|
||||||
access_chain_.base = rvalue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// push offset onto the end of the chain
|
|
||||||
void PushAccessChainOffset(Id offset) {
|
|
||||||
access_chain_.index_chain.push_back(offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
// push new swizzle onto the end of any existing swizzle, merging into a
|
|
||||||
// single swizzle
|
|
||||||
void PushAccessChainSwizzle(std::vector<uint32_t> swizzle,
|
|
||||||
Id pre_swizzle_base_type);
|
|
||||||
|
|
||||||
// push a variable component selection onto the access chain; supporting only
|
|
||||||
// one, so unsided
|
|
||||||
void PushAccessChainComponent(Id component, Id pre_swizzle_base_type) {
|
|
||||||
access_chain_.component = component;
|
|
||||||
if (access_chain_.pre_swizzle_base_type == NoType) {
|
|
||||||
access_chain_.pre_swizzle_base_type = pre_swizzle_base_type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use accessChain and swizzle to store value
|
|
||||||
void CreateAccessChainStore(Id rvalue);
|
|
||||||
|
|
||||||
// use accessChain and swizzle to load an r-value
|
|
||||||
Id CreateAccessChainLoad(Id result_type_id);
|
|
||||||
|
|
||||||
// get the direct pointer for an l-value
|
|
||||||
Id CreateAccessChainLValue();
|
|
||||||
|
|
||||||
void Serialize(std::vector<uint32_t>& out) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
// Maximum dimension for column/row in a matrix.
|
|
||||||
static const int kMaxMatrixSize = 4;
|
|
||||||
|
|
||||||
// Allocates a new <id>.
|
|
||||||
Id AllocateUniqueId() { return ++unique_id_; }
|
|
||||||
|
|
||||||
// Allocates a contiguous sequence of <id>s.
|
|
||||||
Id AllocateUniqueIds(int count) {
|
|
||||||
Id id = unique_id_ + 1;
|
|
||||||
unique_id_ += count;
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
Id MakeIntegerConstant(Id type_id, uint32_t value, bool is_spec_constant);
|
|
||||||
Id FindScalarConstant(Op type_class, Op opcode, Id type_id,
|
|
||||||
uint32_t value) const;
|
|
||||||
Id FindScalarConstant(Op type_class, Op opcode, Id type_id, uint32_t v1,
|
|
||||||
uint32_t v2) const;
|
|
||||||
Id FindCompositeConstant(Op type_class,
|
|
||||||
std::initializer_list<Id> components) const;
|
|
||||||
|
|
||||||
Id CollapseAccessChain();
|
|
||||||
void SimplifyAccessChainSwizzle();
|
|
||||||
void TransferAccessChainSwizzle(bool dynamic);
|
|
||||||
|
|
||||||
void SerializeInstructions(
|
|
||||||
std::vector<uint32_t>& out,
|
|
||||||
const std::vector<Instruction*>& instructions) const;
|
|
||||||
|
|
||||||
void CreateAndSetNoPredecessorBlock(const char* name);
|
|
||||||
void CreateBranch(Block* block);
|
|
||||||
void CreateSelectionMerge(Block* merge_block,
|
|
||||||
spv::SelectionControlMask control);
|
|
||||||
void CreateLoopMerge(Block* merge_block, Block* continueBlock,
|
|
||||||
spv::LoopControlMask control);
|
|
||||||
void CreateConditionalBranch(Id condition, Block* then_block,
|
|
||||||
Block* else_block);
|
|
||||||
|
|
||||||
struct Loop; // Defined below.
|
|
||||||
void CreateBranchToLoopHeaderFromInside(const Loop& loop);
|
|
||||||
|
|
||||||
// Asserts on unimplemented functionality.
|
|
||||||
void CheckNotImplemented(const char* message);
|
|
||||||
|
|
||||||
spv::SourceLanguage source_language_ = spv::SourceLanguage::Unknown;
|
|
||||||
int source_version_ = 0;
|
|
||||||
std::vector<const char*> source_extensions_;
|
|
||||||
spv::AddressingModel addressing_model_ = spv::AddressingModel::Logical;
|
|
||||||
spv::MemoryModel memory_model_ = spv::MemoryModel::GLSL450;
|
|
||||||
std::vector<spv::Capability> capabilities_;
|
|
||||||
int builder_number_ = 0;
|
|
||||||
Module module_;
|
|
||||||
Block* build_point_ = nullptr;
|
|
||||||
Id unique_id_ = 0;
|
|
||||||
Function* main_function_ = nullptr;
|
|
||||||
AccessChain access_chain_;
|
|
||||||
Id glsl_std_450_instruction_set_ = 0;
|
|
||||||
|
|
||||||
// special blocks of instructions for output
|
|
||||||
std::vector<Instruction*> imports_;
|
|
||||||
std::vector<Instruction*> entry_points_;
|
|
||||||
std::vector<Instruction*> execution_modes_;
|
|
||||||
std::vector<Instruction*> names_;
|
|
||||||
std::vector<Instruction*> lines_;
|
|
||||||
std::vector<Instruction*> decorations_;
|
|
||||||
std::vector<Instruction*> constants_types_globals_;
|
|
||||||
std::vector<Instruction*> externals_;
|
|
||||||
|
|
||||||
// not output, internally used for quick & dirty canonical (unique) creation
|
|
||||||
// All types appear before OpConstant.
|
|
||||||
std::vector<Instruction*>
|
|
||||||
grouped_constants_[static_cast<int>(spv::Op::OpConstant)];
|
|
||||||
std::vector<Instruction*>
|
|
||||||
grouped_types_[static_cast<int>(spv::Op::OpConstant)];
|
|
||||||
|
|
||||||
// Stack of switches.
|
|
||||||
std::stack<Block*> switch_merges_;
|
|
||||||
|
|
||||||
// Data that needs to be kept in order to properly handle loops.
|
|
||||||
struct Loop {
|
|
||||||
// Constructs a default Loop structure containing new header, merge, and
|
|
||||||
// body blocks for the current function.
|
|
||||||
// The test_first argument indicates whether the loop test executes at
|
|
||||||
// the top of the loop rather than at the bottom. In the latter case,
|
|
||||||
// also create a phi instruction whose value indicates whether we're on
|
|
||||||
// the first iteration of the loop. The phi instruction is initialized
|
|
||||||
// with no values or predecessor operands.
|
|
||||||
Loop(SpirvEmitter& emitter, bool test_first);
|
|
||||||
|
|
||||||
// The function containing the loop.
|
|
||||||
Function* const function;
|
|
||||||
// The header is the first block generated for the loop.
|
|
||||||
// It dominates all the blocks in the loop, i.e. it is always
|
|
||||||
// executed before any others.
|
|
||||||
// If the loop test is executed before the body (as in "while" and
|
|
||||||
// "for" loops), then the header begins with the test code.
|
|
||||||
// Otherwise, the loop is a "do-while" loop and the header contains the
|
|
||||||
// start of the body of the loop (if the body exists).
|
|
||||||
Block* const header;
|
|
||||||
// The merge block marks the end of the loop. Control is transferred
|
|
||||||
// to the merge block when either the loop test fails, or when a
|
|
||||||
// nested "break" is encountered.
|
|
||||||
Block* const merge;
|
|
||||||
// The body block is the first basic block in the body of the loop, i.e.
|
|
||||||
// the code that is to be repeatedly executed, aside from loop control.
|
|
||||||
// This member is null until we generate code that references the loop
|
|
||||||
// body block.
|
|
||||||
Block* const body;
|
|
||||||
// True when the loop test executes before the body.
|
|
||||||
const bool test_first;
|
|
||||||
// When the test executes after the body, this is defined as the phi
|
|
||||||
// instruction that tells us whether we are on the first iteration of
|
|
||||||
// the loop. Otherwise this is null. This is non-const because
|
|
||||||
// it has to be initialized outside of the initializer-list.
|
|
||||||
Instruction* is_first_iteration;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Our loop stack.
|
|
||||||
std::stack<Loop> loops_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace spirv
|
|
||||||
} // namespace ui
|
|
||||||
} // namespace xe
|
|
||||||
|
|
||||||
#endif // XENIA_UI_SPIRV_SPIRV_EMITTER_H_
|
|
|
@ -1,421 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* 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. *
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Contents originally forked from:
|
|
||||||
// https://github.com/KhronosGroup/glslang/
|
|
||||||
//
|
|
||||||
// Copyright (C) 2014 LunarG, Inc.
|
|
||||||
//
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// Redistribution and use in source and binary forms, with or without
|
|
||||||
// modification, are permitted provided that the following conditions
|
|
||||||
// are met:
|
|
||||||
//
|
|
||||||
// Redistributions of source code must retain the above copyright
|
|
||||||
// notice, this list of conditions and the following disclaimer.
|
|
||||||
//
|
|
||||||
// Redistributions in binary form must reproduce the above
|
|
||||||
// copyright notice, this list of conditions and the following
|
|
||||||
// disclaimer in the documentation and/or other materials provided
|
|
||||||
// with the distribution.
|
|
||||||
//
|
|
||||||
// Neither the name of 3Dlabs Inc. Ltd. nor the names of its
|
|
||||||
// contributors may be used to endorse or promote products derived
|
|
||||||
// from this software without specific prior written permission.
|
|
||||||
//
|
|
||||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
||||||
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
||||||
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
||||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
||||||
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
||||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
||||||
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
||||||
// POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|
||||||
// SPIRV-IR
|
|
||||||
//
|
|
||||||
// Simple in-memory representation (IR) of SPIRV. Just for holding
|
|
||||||
// Each function's CFG of blocks. Has this hierarchy:
|
|
||||||
// - Module, which is a list of
|
|
||||||
// - Function, which is a list of
|
|
||||||
// - Block, which is a list of
|
|
||||||
// - Instruction
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef XENIA_UI_SPIRV_SPIRV_IR_H_
|
|
||||||
#define XENIA_UI_SPIRV_SPIRV_IR_H_
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "xenia/ui/spirv/spirv_util.h"
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace ui {
|
|
||||||
namespace spirv {
|
|
||||||
|
|
||||||
using spv::Id;
|
|
||||||
using spv::Op;
|
|
||||||
|
|
||||||
class Function;
|
|
||||||
class Module;
|
|
||||||
|
|
||||||
const Id NoResult = 0;
|
|
||||||
const Id NoType = 0;
|
|
||||||
|
|
||||||
const uint32_t BadValue = 0xFFFFFFFF;
|
|
||||||
const spv::Decoration NoPrecision = static_cast<spv::Decoration>(BadValue);
|
|
||||||
const spv::MemorySemanticsMask MemorySemanticsAllMemory =
|
|
||||||
static_cast<spv::MemorySemanticsMask>(0x3FF);
|
|
||||||
|
|
||||||
class Instruction {
|
|
||||||
public:
|
|
||||||
Instruction(Id result_id, Id type_id, Op opcode)
|
|
||||||
: result_id_(result_id), type_id_(type_id), opcode_(opcode) {}
|
|
||||||
explicit Instruction(Op opcode) : opcode_(opcode) {}
|
|
||||||
~Instruction() = default;
|
|
||||||
|
|
||||||
void AddIdOperand(Id id) { operands_.push_back(id); }
|
|
||||||
|
|
||||||
void AddIdOperands(const std::vector<Id>& ids) {
|
|
||||||
for (auto id : ids) {
|
|
||||||
operands_.push_back(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void AddIdOperands(std::initializer_list<Id> ids) {
|
|
||||||
for (auto id : ids) {
|
|
||||||
operands_.push_back(id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddImmediateOperand(uint32_t immediate) {
|
|
||||||
operands_.push_back(immediate);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
void AddImmediateOperand(T immediate) {
|
|
||||||
static_assert(sizeof(T) == sizeof(uint32_t), "Invalid operand size");
|
|
||||||
operands_.push_back(static_cast<uint32_t>(immediate));
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddImmediateOperands(const std::vector<uint32_t>& immediates) {
|
|
||||||
for (auto immediate : immediates) {
|
|
||||||
operands_.push_back(immediate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddImmediateOperands(std::initializer_list<uint32_t> immediates) {
|
|
||||||
for (auto immediate : immediates) {
|
|
||||||
operands_.push_back(immediate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddStringOperand(const char* str) {
|
|
||||||
original_string_ = str;
|
|
||||||
uint32_t word;
|
|
||||||
char* word_string = reinterpret_cast<char*>(&word);
|
|
||||||
char* word_ptr = word_string;
|
|
||||||
int char_count = 0;
|
|
||||||
char c;
|
|
||||||
do {
|
|
||||||
c = *(str++);
|
|
||||||
*(word_ptr++) = c;
|
|
||||||
++char_count;
|
|
||||||
if (char_count == 4) {
|
|
||||||
AddImmediateOperand(word);
|
|
||||||
word_ptr = word_string;
|
|
||||||
char_count = 0;
|
|
||||||
}
|
|
||||||
} while (c != 0);
|
|
||||||
|
|
||||||
// deal with partial last word
|
|
||||||
if (char_count > 0) {
|
|
||||||
// pad with 0s
|
|
||||||
for (; char_count < 4; ++char_count) {
|
|
||||||
*(word_ptr++) = 0;
|
|
||||||
}
|
|
||||||
AddImmediateOperand(word);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Op opcode() const { return opcode_; }
|
|
||||||
int operand_count() const { return static_cast<int>(operands_.size()); }
|
|
||||||
Id result_id() const { return result_id_; }
|
|
||||||
Id type_id() const { return type_id_; }
|
|
||||||
Id id_operand(int op) const { return operands_[op]; }
|
|
||||||
uint32_t immediate_operand(int op) const { return operands_[op]; }
|
|
||||||
const char* string_operand() const { return original_string_.c_str(); }
|
|
||||||
|
|
||||||
// Write out the binary form.
|
|
||||||
void Serialize(std::vector<uint32_t>& out) const {
|
|
||||||
uint32_t word_count = 1;
|
|
||||||
if (type_id_) {
|
|
||||||
++word_count;
|
|
||||||
}
|
|
||||||
if (result_id_) {
|
|
||||||
++word_count;
|
|
||||||
}
|
|
||||||
word_count += static_cast<uint32_t>(operands_.size());
|
|
||||||
|
|
||||||
out.push_back((word_count << spv::WordCountShift) |
|
|
||||||
static_cast<uint32_t>(opcode_));
|
|
||||||
if (type_id_) {
|
|
||||||
out.push_back(type_id_);
|
|
||||||
}
|
|
||||||
if (result_id_) {
|
|
||||||
out.push_back(result_id_);
|
|
||||||
}
|
|
||||||
for (auto operand : operands_) {
|
|
||||||
out.push_back(operand);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Instruction(const Instruction&) = delete;
|
|
||||||
|
|
||||||
Id result_id_ = NoResult;
|
|
||||||
Id type_id_ = NoType;
|
|
||||||
Op opcode_;
|
|
||||||
std::vector<Id> operands_;
|
|
||||||
std::string original_string_; // could be optimized away; convenience for
|
|
||||||
// getting string operand
|
|
||||||
};
|
|
||||||
|
|
||||||
class Block {
|
|
||||||
public:
|
|
||||||
Block(Id id, Function& parent);
|
|
||||||
~Block() {
|
|
||||||
for (size_t i = 0; i < instructions_.size(); ++i) {
|
|
||||||
delete instructions_[i];
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < local_variables_.size(); ++i) {
|
|
||||||
delete local_variables_[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Id id() { return instructions_.front()->result_id(); }
|
|
||||||
|
|
||||||
Function& parent() const { return parent_; }
|
|
||||||
|
|
||||||
void AddInstruction(Instruction* instr);
|
|
||||||
void AddLocalVariable(Instruction* instr) {
|
|
||||||
local_variables_.push_back(instr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddPredecessor(Block* predecessor) {
|
|
||||||
predecessors_.push_back(predecessor);
|
|
||||||
}
|
|
||||||
|
|
||||||
int predecessor_count() const {
|
|
||||||
return static_cast<int>(predecessors_.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_unreachable() const { return unreachable_; }
|
|
||||||
void set_unreachable(bool value) { unreachable_ = value; }
|
|
||||||
|
|
||||||
bool is_terminated() const {
|
|
||||||
switch (instructions_.back()->opcode()) {
|
|
||||||
case spv::Op::OpBranch:
|
|
||||||
case spv::Op::OpBranchConditional:
|
|
||||||
case spv::Op::OpSwitch:
|
|
||||||
case spv::Op::OpKill:
|
|
||||||
case spv::Op::OpReturn:
|
|
||||||
case spv::Op::OpReturnValue:
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Serialize(std::vector<uint32_t>& out) const {
|
|
||||||
// skip the degenerate unreachable blocks
|
|
||||||
// TODO: code gen: skip all unreachable blocks (transitive closure)
|
|
||||||
// (but, until that's done safer to keep non-degenerate
|
|
||||||
// unreachable blocks, in case others depend on something)
|
|
||||||
if (unreachable_ && instructions_.size() <= 2) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
instructions_[0]->Serialize(out);
|
|
||||||
for (auto variable : local_variables_) {
|
|
||||||
variable->Serialize(out);
|
|
||||||
}
|
|
||||||
for (int i = 1; i < instructions_.size(); ++i) {
|
|
||||||
instructions_[i]->Serialize(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Block(const Block&) = delete;
|
|
||||||
Block& operator=(Block&) = delete;
|
|
||||||
|
|
||||||
// To enforce keeping parent and ownership in sync:
|
|
||||||
friend Function;
|
|
||||||
|
|
||||||
std::vector<Instruction*> instructions_;
|
|
||||||
std::vector<Block*> predecessors_;
|
|
||||||
std::vector<Instruction*> local_variables_;
|
|
||||||
Function& parent_;
|
|
||||||
|
|
||||||
// track whether this block is known to be uncreachable (not necessarily
|
|
||||||
// true for all unreachable blocks, but should be set at least
|
|
||||||
// for the extraneous ones introduced by the builder).
|
|
||||||
bool unreachable_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Function {
|
|
||||||
public:
|
|
||||||
Function(Id id, Id resultType, Id functionType, Id firstParam,
|
|
||||||
Module& parent);
|
|
||||||
~Function() {
|
|
||||||
for (size_t i = 0; i < parameter_instructions_.size(); ++i) {
|
|
||||||
delete parameter_instructions_[i];
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < blocks_.size(); ++i) {
|
|
||||||
delete blocks_[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Id id() const { return function_instruction_.result_id(); }
|
|
||||||
Id param_id(int p) { return parameter_instructions_[p]->result_id(); }
|
|
||||||
|
|
||||||
void push_block(Block* block) { blocks_.push_back(block); }
|
|
||||||
void pop_block(Block* block) { blocks_.pop_back(); }
|
|
||||||
|
|
||||||
Module& parent() const { return parent_; }
|
|
||||||
Block* entry_block() const { return blocks_.front(); }
|
|
||||||
Block* last_block() const { return blocks_.back(); }
|
|
||||||
|
|
||||||
void AddLocalVariable(Instruction* instr);
|
|
||||||
|
|
||||||
Id return_type() const { return function_instruction_.type_id(); }
|
|
||||||
|
|
||||||
void Serialize(std::vector<uint32_t>& out) const {
|
|
||||||
// OpFunction
|
|
||||||
function_instruction_.Serialize(out);
|
|
||||||
|
|
||||||
// OpFunctionParameter
|
|
||||||
for (auto instruction : parameter_instructions_) {
|
|
||||||
instruction->Serialize(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blocks
|
|
||||||
for (auto block : blocks_) {
|
|
||||||
block->Serialize(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
Instruction end(0, 0, spv::Op::OpFunctionEnd);
|
|
||||||
end.Serialize(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Function(const Function&) = delete;
|
|
||||||
Function& operator=(Function&) = delete;
|
|
||||||
|
|
||||||
Module& parent_;
|
|
||||||
Instruction function_instruction_;
|
|
||||||
std::vector<Instruction*> parameter_instructions_;
|
|
||||||
std::vector<Block*> blocks_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Module {
|
|
||||||
public:
|
|
||||||
Module() = default;
|
|
||||||
~Module() {
|
|
||||||
for (size_t i = 0; i < functions_.size(); ++i) {
|
|
||||||
delete functions_[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddFunction(Function* function) { functions_.push_back(function); }
|
|
||||||
|
|
||||||
void MapInstruction(Instruction* instr) {
|
|
||||||
spv::Id result_id = instr->result_id();
|
|
||||||
// Map the instruction's result id.
|
|
||||||
if (result_id >= id_to_instruction_.size()) {
|
|
||||||
id_to_instruction_.resize(result_id + 16);
|
|
||||||
}
|
|
||||||
id_to_instruction_[result_id] = instr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Instruction* instruction(Id id) const { return id_to_instruction_[id]; }
|
|
||||||
|
|
||||||
spv::Id type_id(Id result_id) const {
|
|
||||||
return id_to_instruction_[result_id]->type_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
spv::StorageClass storage_class(Id type_id) const {
|
|
||||||
return (spv::StorageClass)id_to_instruction_[type_id]->immediate_operand(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Serialize(std::vector<uint32_t>& out) const {
|
|
||||||
for (auto function : functions_) {
|
|
||||||
function->Serialize(out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Module(const Module&) = delete;
|
|
||||||
|
|
||||||
std::vector<Function*> functions_;
|
|
||||||
|
|
||||||
// Maps from result id to instruction having that result id.
|
|
||||||
std::vector<Instruction*> id_to_instruction_;
|
|
||||||
};
|
|
||||||
|
|
||||||
inline Function::Function(Id id, Id result_type_id, Id function_type_id,
|
|
||||||
Id first_param_id, Module& parent)
|
|
||||||
: parent_(parent),
|
|
||||||
function_instruction_(id, result_type_id, spv::Op::OpFunction) {
|
|
||||||
// OpFunction
|
|
||||||
function_instruction_.AddImmediateOperand(
|
|
||||||
static_cast<uint32_t>(spv::FunctionControlMask::MaskNone));
|
|
||||||
function_instruction_.AddIdOperand(function_type_id);
|
|
||||||
parent.MapInstruction(&function_instruction_);
|
|
||||||
parent.AddFunction(this);
|
|
||||||
|
|
||||||
// OpFunctionParameter
|
|
||||||
Instruction* type_instr = parent.instruction(function_type_id);
|
|
||||||
int param_count = type_instr->operand_count() - 1;
|
|
||||||
for (int p = 0; p < param_count; ++p) {
|
|
||||||
auto param =
|
|
||||||
new Instruction(first_param_id + p, type_instr->id_operand(p + 1),
|
|
||||||
spv::Op::OpFunctionParameter);
|
|
||||||
parent.MapInstruction(param);
|
|
||||||
parameter_instructions_.push_back(param);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Function::AddLocalVariable(Instruction* instr) {
|
|
||||||
blocks_[0]->AddLocalVariable(instr);
|
|
||||||
parent_.MapInstruction(instr);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Block::Block(Id id, Function& parent)
|
|
||||||
: parent_(parent), unreachable_(false) {
|
|
||||||
instructions_.push_back(new Instruction(id, NoType, spv::Op::OpLabel));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void Block::AddInstruction(Instruction* inst) {
|
|
||||||
instructions_.push_back(inst);
|
|
||||||
if (inst->result_id()) {
|
|
||||||
parent_.parent().MapInstruction(inst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spirv
|
|
||||||
} // namespace ui
|
|
||||||
} // namespace xe
|
|
||||||
|
|
||||||
#endif // XENIA_UI_SPIRV_SPIRV_IR_H_
|
|
|
@ -1,22 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* 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. *
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "xenia/ui/spirv/spirv_optimizer.h"
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace ui {
|
|
||||||
namespace spirv {
|
|
||||||
|
|
||||||
SpirvOptimizer::SpirvOptimizer() = default;
|
|
||||||
|
|
||||||
SpirvOptimizer::~SpirvOptimizer() = default;
|
|
||||||
|
|
||||||
} // namespace spirv
|
|
||||||
} // namespace ui
|
|
||||||
} // namespace xe
|
|
|
@ -1,31 +0,0 @@
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* 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_UI_SPIRV_SPIRV_OPTIMIZER_H_
|
|
||||||
#define XENIA_UI_SPIRV_SPIRV_OPTIMIZER_H_
|
|
||||||
|
|
||||||
#include "xenia/ui/spirv/spirv_util.h"
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace ui {
|
|
||||||
namespace spirv {
|
|
||||||
|
|
||||||
class SpirvOptimizer {
|
|
||||||
public:
|
|
||||||
SpirvOptimizer();
|
|
||||||
~SpirvOptimizer();
|
|
||||||
|
|
||||||
private:
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace spirv
|
|
||||||
} // namespace ui
|
|
||||||
} // namespace xe
|
|
||||||
|
|
||||||
#endif // XENIA_UI_SPIRV_SPIRV_OPTIMIZER_H_
|
|
Loading…
Reference in New Issue