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")
|
||||
language("C++")
|
||||
links({
|
||||
"glslang-spirv",
|
||||
"snappy",
|
||||
"spirv-tools",
|
||||
"xenia-base",
|
||||
|
@ -29,6 +30,7 @@ project("xenia-gpu-shader-compiler")
|
|||
language("C++")
|
||||
links({
|
||||
"gflags",
|
||||
"glslang-spirv",
|
||||
"spirv-tools",
|
||||
"xenia-base",
|
||||
"xenia-gpu",
|
||||
|
|
|
@ -137,7 +137,7 @@ bool ShaderTranslator::Translate(Shader* shader) {
|
|||
constant_register_map_.packed_byte_length +=
|
||||
4 * xe::bit_count(constant_register_map_.int_bitmap);
|
||||
// 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]) {
|
||||
constant_register_map_.packed_byte_length += 4;
|
||||
}
|
||||
|
@ -161,6 +161,8 @@ bool ShaderTranslator::Translate(Shader* shader) {
|
|||
}
|
||||
}
|
||||
|
||||
PostTranslation(shader);
|
||||
|
||||
return shader->is_valid_;
|
||||
}
|
||||
|
||||
|
|
|
@ -71,6 +71,13 @@ class ShaderTranslator {
|
|||
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.
|
||||
// This is triggered once for each label required (due to control flow
|
||||
// operations) before any of the instructions within the target exec.
|
||||
|
|
|
@ -11,124 +11,182 @@
|
|||
|
||||
#include <cstring>
|
||||
|
||||
#include "xenia/base/logging.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
using spv::GLSLstd450;
|
||||
using spv::Id;
|
||||
using spv::Op;
|
||||
|
||||
SpirvShaderTranslator::SpirvShaderTranslator() = default;
|
||||
|
||||
SpirvShaderTranslator::~SpirvShaderTranslator() = default;
|
||||
|
||||
void SpirvShaderTranslator::StartTranslation() {
|
||||
auto& e = emitter_;
|
||||
// Create a new builder.
|
||||
builder_ = std::make_unique<spv::Builder>(0xFFFFFFFF);
|
||||
auto& b = *builder_;
|
||||
|
||||
auto fn = e.MakeMainEntry();
|
||||
auto float_1_0 = e.MakeFloatConstant(1.0f);
|
||||
auto acos = e.CreateGlslStd450InstructionCall(
|
||||
spv::Decoration::Invariant, e.MakeFloatType(32), spv::GLSLstd450::kAcos,
|
||||
{float_1_0});
|
||||
e.MakeReturn(true);
|
||||
// Import required modules.
|
||||
glsl_std_450_instruction_set_ = b.import("GLSL.std.450");
|
||||
|
||||
// Configure environment.
|
||||
b.setSource(spv::SourceLanguage::SourceLanguageUnknown, 0);
|
||||
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() {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
b.makeReturn(false);
|
||||
|
||||
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;
|
||||
spirv_bytes.resize(spirv_words.size() * 4);
|
||||
std::memcpy(spirv_bytes.data(), spirv_words.data(), spirv_bytes.size());
|
||||
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) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessControlFlowNopInstruction() {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessExecInstructionBegin(
|
||||
const ParsedExecInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessExecInstructionEnd(
|
||||
const ParsedExecInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessLoopStartInstruction(
|
||||
const ParsedLoopStartInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessLoopEndInstruction(
|
||||
const ParsedLoopEndInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessCallInstruction(
|
||||
const ParsedCallInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessReturnInstruction(
|
||||
const ParsedReturnInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessJumpInstruction(
|
||||
const ParsedJumpInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessAllocInstruction(
|
||||
const ParsedAllocInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessVertexFetchInstruction(
|
||||
const ParsedVertexFetchInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessTextureFetchInstruction(
|
||||
const ParsedTextureFetchInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessAluInstruction(
|
||||
const ParsedAluInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
switch (instr.type) {
|
||||
case ParsedAluInstruction::Type::kNop:
|
||||
e.CreateNop();
|
||||
b.createNoResultOp(spv::Op::OpNop);
|
||||
break;
|
||||
case ParsedAluInstruction::Type::kVector:
|
||||
ProcessVectorAluInstruction(instr);
|
||||
|
@ -141,14 +199,14 @@ void SpirvShaderTranslator::ProcessAluInstruction(
|
|||
|
||||
void SpirvShaderTranslator::ProcessVectorAluInstruction(
|
||||
const ParsedAluInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
void SpirvShaderTranslator::ProcessScalarAluInstruction(
|
||||
const ParsedAluInstruction& instr) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
spv::Id value_id = LoadFromOperand(instr.operands[0]);
|
||||
|
||||
|
@ -157,11 +215,19 @@ void SpirvShaderTranslator::ProcessScalarAluInstruction(
|
|||
EmitUnimplementedTranslationError();
|
||||
}
|
||||
|
||||
spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) {
|
||||
auto& e = emitter_;
|
||||
Id SpirvShaderTranslator::CreateGlslStd450InstructionCall(
|
||||
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 current_value_id = e.CreateUndefined(current_type_id);
|
||||
spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) {
|
||||
auto& b = *builder_;
|
||||
|
||||
spv::Id current_type_id = b.makeFloatType(32);
|
||||
spv::Id current_value_id = b.createUndefined(current_type_id);
|
||||
|
||||
// storage_addressing_mode
|
||||
switch (op.storage_source) {
|
||||
|
@ -186,13 +252,13 @@ spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) {
|
|||
}
|
||||
|
||||
if (op.is_absolute_value) {
|
||||
current_value_id = e.CreateGlslStd450InstructionCall(
|
||||
spv::Decoration::RelaxedPrecision, current_type_id,
|
||||
spv::GLSLstd450::kFAbs, {current_value_id});
|
||||
current_value_id = CreateGlslStd450InstructionCall(
|
||||
spv::Decoration::DecorationRelaxedPrecision, current_type_id,
|
||||
GLSLstd450::kFAbs, {current_value_id});
|
||||
}
|
||||
if (op.is_negated) {
|
||||
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
|
||||
|
@ -202,7 +268,7 @@ spv::Id SpirvShaderTranslator::LoadFromOperand(const InstructionOperand& op) {
|
|||
|
||||
void SpirvShaderTranslator::StoreToResult(spv::Id source_value_id,
|
||||
const InstructionResult& result) {
|
||||
auto& e = emitter_;
|
||||
auto& b = *builder_;
|
||||
|
||||
if (result.storage_target == InstructionStorageTarget::kNone) {
|
||||
// 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_type_id = e.GetTypeId(source_value_id);
|
||||
spv::Id current_type_id = b.getTypeId(source_value_id);
|
||||
|
||||
// Clamp the input value.
|
||||
if (result.is_clamped) {
|
||||
|
@ -248,7 +314,7 @@ void SpirvShaderTranslator::StoreToResult(spv::Id source_value_id,
|
|||
// swizzle
|
||||
|
||||
// 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) {
|
||||
EmitTranslationError("Type conversion on storage not yet implemented");
|
||||
}
|
||||
|
|
|
@ -14,8 +14,10 @@
|
|||
#include <string>
|
||||
#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/ui/spirv/spirv_emitter.h"
|
||||
#include "xenia/ui/spirv/spirv_disassembler.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
@ -28,6 +30,7 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
protected:
|
||||
void StartTranslation() override;
|
||||
std::vector<uint8_t> CompleteTranslation() override;
|
||||
void PostTranslation(Shader* shader) override;
|
||||
|
||||
void ProcessLabel(uint32_t cf_index) override;
|
||||
void ProcessControlFlowNopInstruction() override;
|
||||
|
@ -48,9 +51,16 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
void ProcessAluInstruction(const ParsedAluInstruction& instr) override;
|
||||
|
||||
private:
|
||||
void SetupPushConstants();
|
||||
|
||||
void ProcessVectorAluInstruction(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.
|
||||
// The value returned will be in the form described in the operand (number of
|
||||
// components, etc).
|
||||
|
@ -60,7 +70,11 @@ class SpirvShaderTranslator : public ShaderTranslator {
|
|||
// the proper components will be selected.
|
||||
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
|
||||
|
|
|
@ -22,6 +22,8 @@ VulkanShader::VulkanShader(ShaderType shader_type, uint64_t data_hash,
|
|||
|
||||
VulkanShader::~VulkanShader() = default;
|
||||
|
||||
bool VulkanShader::Prepare() { return true; }
|
||||
|
||||
} // namespace vulkan
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
|
|
@ -24,6 +24,13 @@ class VulkanShader : public Shader {
|
|||
VulkanShader(ShaderType shader_type, uint64_t data_hash,
|
||||
const uint32_t* dword_ptr, uint32_t dword_count);
|
||||
~VulkanShader() override;
|
||||
|
||||
VkShaderModule shader_module() const { return shader_module_; }
|
||||
|
||||
bool Prepare();
|
||||
|
||||
private:
|
||||
VkShaderModule shader_module_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace vulkan
|
||||
|
|
|
@ -7,6 +7,7 @@ project("xenia-ui-spirv")
|
|||
kind("StaticLib")
|
||||
language("C++")
|
||||
links({
|
||||
"glslang-spirv",
|
||||
"spirv-tools",
|
||||
"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