Initial commit; tgv handled
This commit is contained in:
31
CMakeLists.txt
Normal file
31
CMakeLists.txt
Normal file
@@ -0,0 +1,31 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
project(Bluebell VERSION 0.1)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED True)
|
||||
|
||||
include(FindPkgConfig)
|
||||
find_package(LLVM 13 REQUIRED CONFIG)
|
||||
find_package(Boost 1.78
|
||||
COMPONENTS log)
|
||||
list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}")
|
||||
include(HandleLLVMOptions)
|
||||
include(AddLLVM)
|
||||
|
||||
#pkg_check_modules(shaderc REQUIRED IMPORTED_TARGET shaderc)
|
||||
|
||||
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
|
||||
|
||||
include_directories(${LLVM_INCLUDE_DIRS})
|
||||
add_definitions(${LLVM_DEFINITIONS})
|
||||
|
||||
add_executable(bluebell
|
||||
src/bluebell.cpp src/bluebell.hpp
|
||||
src/pre-compiler.cpp src/pre-compiler.hpp src/compiler.cpp src/compiler.hpp)
|
||||
|
||||
#target_link_libraries(bluebell PkgConfig::shaderc)
|
||||
|
||||
llvm_map_components_to_libnames(llvm_libs core)
|
||||
|
||||
target_link_libraries(bluebell shaderc_shared Boost::boost Boost::log ${llvm_libs})
|
||||
|
||||
24
shell.nix
Normal file
24
shell.nix
Normal file
@@ -0,0 +1,24 @@
|
||||
{ pkgs ? import <nixpkgs> {} }:
|
||||
|
||||
pkgs.clangStdenv.mkDerivation {
|
||||
name = "bluebell";
|
||||
buildInputs = with pkgs; [
|
||||
|
||||
# keep this line if you use bash
|
||||
bashInteractive
|
||||
llvm_13
|
||||
shaderc
|
||||
spirv-headers
|
||||
spirv-tools
|
||||
boost178
|
||||
];
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
cmake
|
||||
ninja
|
||||
pkg-config
|
||||
gdb
|
||||
];
|
||||
|
||||
SPIRV_HEADERS = "${pkgs.spirv-headers}";
|
||||
}
|
||||
47
src/bluebell.cpp
Normal file
47
src/bluebell.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
//
|
||||
// Created by thequux on 5/23/22.
|
||||
//
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "bluebell.hpp"
|
||||
#include "pre-compiler.hpp"
|
||||
#include "compiler.hpp"
|
||||
|
||||
const char* SHADER_PREFIX = "#version 320 es\n\
|
||||
precision highp float;\
|
||||
layout(binding=0)\
|
||||
uniform Shadertoy {\
|
||||
uniform vec3 iResolution;\
|
||||
uniform float iTime;\
|
||||
uniform float iTimeDelta;\
|
||||
uniform float iFrame;\
|
||||
uniform float iChannelTime[4];\
|
||||
uniform vec4 iMouse;\
|
||||
uniform vec4 iDate;\
|
||||
uniform float iSampleRate;\
|
||||
uniform vec3 iChannelResolution[4];\
|
||||
};\
|
||||
\
|
||||
layout(location=0) out vec4 wallshader_out_diffuseColor;";
|
||||
|
||||
const char* SHADER_SUFFIX = "void main() { \
|
||||
mainImage(wallshader_out_diffuseColor, gl_FragCoord.xy); \
|
||||
}";
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::stringstream shader_src;
|
||||
std::ifstream input(argv[1]);
|
||||
|
||||
shader_src << SHADER_PREFIX;
|
||||
shader_src << input.rdbuf();
|
||||
shader_src << SHADER_SUFFIX;
|
||||
|
||||
// std::cout << shader_src.str();
|
||||
|
||||
auto shader = pre_compile_shader(shader_src.str());
|
||||
Compiler compiler;
|
||||
compiler.compile(*shader);
|
||||
|
||||
}
|
||||
6
src/bluebell.hpp
Normal file
6
src/bluebell.hpp
Normal file
@@ -0,0 +1,6 @@
|
||||
//
|
||||
// Created by thequux on 5/23/22.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
542
src/compiler.cpp
Normal file
542
src/compiler.cpp
Normal file
@@ -0,0 +1,542 @@
|
||||
//
|
||||
// Created by thequux on 5/24/22.
|
||||
//
|
||||
|
||||
#include "compiler.hpp"
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <spirv-tools/libspirv.hpp>
|
||||
#include <spirv/1.0/spirv.hpp11>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wunknown-pragmas"
|
||||
|
||||
class SpirvType {
|
||||
protected:
|
||||
llvm::Type *llvm = nullptr;
|
||||
|
||||
virtual llvm::Type *build_llvm_type_impl(llvm::LLVMContext& ctx) = 0;
|
||||
public:
|
||||
virtual bool is_composite() const { return false; }
|
||||
virtual bool is_vector() const { return false; }
|
||||
virtual std::shared_ptr<SpirvType> inner_type() const { return nullptr; }
|
||||
virtual ~SpirvType() = default;
|
||||
|
||||
llvm::Type *get_llvm_type() const { return llvm; }
|
||||
void build_llvm_type(llvm::LLVMContext &ctx) {
|
||||
if (llvm == nullptr) {
|
||||
llvm = build_llvm_type_impl(ctx);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
class SpirvVoidType: public SpirvType {
|
||||
llvm::Type* build_llvm_type_impl(llvm::LLVMContext &ctx) override {
|
||||
return llvm::Type::getVoidTy(ctx);
|
||||
}
|
||||
};
|
||||
class SpirvBoolType: public SpirvType {
|
||||
llvm::Type * build_llvm_type_impl(llvm::LLVMContext &ctx) override {
|
||||
return llvm::Type::getInt1Ty(ctx);
|
||||
}
|
||||
};
|
||||
class SpirvIntType: public SpirvType {
|
||||
llvm::Type * build_llvm_type_impl(llvm::LLVMContext &ctx) override {
|
||||
switch (width) {
|
||||
case 32: return llvm::Type::getInt32Ty(ctx);
|
||||
case 64: return llvm::Type::getInt64Ty(ctx);
|
||||
default:
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid integer width " << width;
|
||||
// TODO will crash if invalid width is provided
|
||||
return nullptr; // this will quickly result in crashes, but that's OK for now
|
||||
}
|
||||
}
|
||||
public:
|
||||
int width;
|
||||
bool is_signed;
|
||||
|
||||
SpirvIntType(int width, bool isSigned) : width(width), is_signed(isSigned) {}
|
||||
|
||||
};
|
||||
|
||||
class SpirvFloatType: public SpirvType {
|
||||
public:
|
||||
int width;
|
||||
|
||||
SpirvFloatType(int width) : width(width) {}
|
||||
|
||||
llvm::Type * build_llvm_type_impl(llvm::LLVMContext &ctx) override {
|
||||
switch (width) {
|
||||
case 16: return llvm::Type::getHalfTy(ctx);
|
||||
case 32: return llvm::Type::getFloatTy(ctx);
|
||||
case 64: return llvm::Type::getDoubleTy(ctx);
|
||||
default:
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid float width " << width;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SpirvSequenceType: public SpirvType {
|
||||
protected:
|
||||
std::shared_ptr<SpirvType> inner;
|
||||
public:
|
||||
std::shared_ptr<SpirvType> inner_type() const override {
|
||||
return inner;
|
||||
}
|
||||
|
||||
explicit SpirvSequenceType(std::shared_ptr<SpirvType> &&inner): inner(inner) {}
|
||||
explicit SpirvSequenceType(std::shared_ptr<SpirvType> &inner): inner(inner) {}
|
||||
};
|
||||
|
||||
class SpirvArrayType: public SpirvSequenceType {
|
||||
int length;
|
||||
public:
|
||||
SpirvArrayType(std::shared_ptr<SpirvType> &&inner, int length)
|
||||
: SpirvSequenceType(std::move(inner)), length(length) {}
|
||||
SpirvArrayType(std::shared_ptr<SpirvType> &inner, int length)
|
||||
: SpirvSequenceType(inner), length(length) {}
|
||||
llvm::Type *build_llvm_type_impl(llvm::LLVMContext &ctx) override {
|
||||
inner->build_llvm_type(ctx);
|
||||
return llvm::ArrayType::get(inner->get_llvm_type(), length);
|
||||
}
|
||||
};
|
||||
|
||||
class SpirvVectorType: public SpirvSequenceType {
|
||||
int length;
|
||||
public:
|
||||
SpirvVectorType(std::shared_ptr<SpirvType> &&inner, int length)
|
||||
: SpirvSequenceType(std::move(inner)), length(length) {}
|
||||
SpirvVectorType(std::shared_ptr<SpirvType> &inner, int length)
|
||||
: SpirvSequenceType(inner), length(length) {}
|
||||
|
||||
llvm::Type *build_llvm_type_impl(llvm::LLVMContext &ctx) override {
|
||||
inner->build_llvm_type(ctx);
|
||||
return llvm::FixedVectorType::get(inner->get_llvm_type(), length);
|
||||
}
|
||||
};
|
||||
|
||||
class SpirvMatrixType: public SpirvSequenceType {
|
||||
int n_cols;
|
||||
public:
|
||||
SpirvMatrixType(std::shared_ptr<SpirvType> &&inner, int n_cols)
|
||||
: SpirvSequenceType(std::move(inner)), n_cols(n_cols) {}
|
||||
SpirvMatrixType(std::shared_ptr<SpirvType> &inner, int n_cols)
|
||||
: SpirvSequenceType(inner), n_cols(n_cols) {}
|
||||
|
||||
llvm::Type *build_llvm_type_impl(llvm::LLVMContext &ctx) override {
|
||||
inner->build_llvm_type(ctx);
|
||||
// TODO: Is this really the right type?
|
||||
return llvm::ArrayType::get(inner->get_llvm_type(), n_cols);
|
||||
}
|
||||
};
|
||||
|
||||
class SpirvStructType: public SpirvType {
|
||||
public:
|
||||
std::vector<std::shared_ptr<SpirvType>> members;
|
||||
|
||||
explicit SpirvStructType(std::vector<std::shared_ptr<SpirvType>> members) : members(std::move(members)) {}
|
||||
|
||||
llvm::Type * build_llvm_type_impl(llvm::LLVMContext &ctx) override {
|
||||
std::vector<llvm::Type*> inner_types;
|
||||
for (auto &inner: members) {
|
||||
inner->build_llvm_type(ctx);
|
||||
inner_types.push_back(inner->get_llvm_type());
|
||||
}
|
||||
auto ret = llvm::StructType::create(ctx, llvm::ArrayRef<llvm::Type*>(inner_types));
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
class SpirvPointerType: public SpirvType {
|
||||
public:
|
||||
SpirvPointerType(std::shared_ptr<SpirvType> inner) : inner(std::move(inner)) {}
|
||||
llvm::Type * build_llvm_type_impl(llvm::LLVMContext &ctx) override {
|
||||
inner->build_llvm_type(ctx);
|
||||
return inner->get_llvm_type()->getPointerTo();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<SpirvType> inner;
|
||||
};
|
||||
|
||||
class SpirvFunctionType: public SpirvType {
|
||||
public:
|
||||
SpirvFunctionType(std::shared_ptr<SpirvType> retType, std::vector<std::shared_ptr<SpirvType>> argTypes)
|
||||
: ret_type(std::move(retType)), arg_types(std::move(argTypes)) {}
|
||||
|
||||
private:
|
||||
std::shared_ptr<SpirvType> ret_type;
|
||||
std::vector<std::shared_ptr<SpirvType>> arg_types;
|
||||
|
||||
llvm::Type * build_llvm_type_impl(llvm::LLVMContext &ctx) override {
|
||||
ret_type->build_llvm_type(ctx);
|
||||
|
||||
std::vector<llvm::Type*> args;
|
||||
for (auto &arg: arg_types) {
|
||||
arg->build_llvm_type(ctx);
|
||||
args.push_back(arg->get_llvm_type());
|
||||
}
|
||||
|
||||
return llvm::FunctionType::get(ret_type->get_llvm_type(), llvm::ArrayRef<llvm::Type*>(args), false);
|
||||
}
|
||||
};
|
||||
|
||||
std::string decode_string_arg(const uint32_t* args) {
|
||||
std::string result;
|
||||
for (int i = 0;; i++) {
|
||||
uint32_t word = args[i];
|
||||
for (int j = 0; j < 4; j++) {
|
||||
char c = (char)(word & 0xff);
|
||||
if (!c) return result;
|
||||
result.push_back(c);
|
||||
word >>= 8;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#define OP_BYTES(i) (insn->words + insn->operands[i].offset)
|
||||
#define OP_WORD(i) (*OP_BYTES(i))
|
||||
|
||||
struct EntryPoint {
|
||||
std::string name;
|
||||
uint32_t func_id;
|
||||
std::vector<uint32_t> interface_vars;
|
||||
|
||||
public:
|
||||
EntryPoint(const spv_parsed_instruction_t *insn) {
|
||||
func_id = OP_WORD(1);
|
||||
name = decode_string_arg(OP_BYTES(2));
|
||||
for (int i = 3; i < insn->num_operands; i++) {
|
||||
interface_vars.push_back(OP_WORD(i));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
static spv_result_t impl_parse_header(
|
||||
void* user_data, spv_endianness_t endian, uint32_t magic, uint32_t version,
|
||||
uint32_t generator, uint32_t id_bound, uint32_t reserved);
|
||||
|
||||
static spv_result_t impl_parse_insn(
|
||||
void* user_data, const spv_parsed_instruction_t* parsed_instruction);
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "cppcoreguidelines-pro-type-member-init"
|
||||
struct Constant {
|
||||
enum class Type {
|
||||
U32, U64, I32, I64,
|
||||
F32, F64,
|
||||
|
||||
} type;
|
||||
|
||||
union {
|
||||
uint64_t ival;
|
||||
double fval;
|
||||
};
|
||||
|
||||
Constant(uint32_t val): type(Type::U32), ival(val) {}
|
||||
Constant(uint64_t val): type(Type::U64), ival(val) {}
|
||||
Constant(int32_t val): type(Type::I32), ival(val) {}
|
||||
Constant(int64_t val): type(Type::I64), ival(val) {}
|
||||
Constant(float val): type(Type::F32), fval(val) {}
|
||||
Constant(double val): type(Type::F64), fval(val) {}
|
||||
Constant(Type type, uint64_t val): type(type), ival(val) {}
|
||||
Constant(Type type, double val): type(type), fval(val) {}
|
||||
Constant(): type(Type::I32), ival(0) {}
|
||||
};
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
class Variable {
|
||||
public:
|
||||
|
||||
void get_llvm_var() {
|
||||
|
||||
}
|
||||
};
|
||||
struct CompilerImpl {
|
||||
std::unique_ptr<llvm::LLVMContext> ctx;
|
||||
std::unique_ptr<llvm::Module> module;
|
||||
std::unique_ptr<llvm::IRBuilder<>> builder;
|
||||
|
||||
std::map<uint32_t, spv_ext_inst_type_t> ext_insts;
|
||||
std::map<uint32_t, std::shared_ptr<SpirvType>> types;
|
||||
std::map<uint32_t, Constant> constants;
|
||||
std::map<std::string, std::shared_ptr<EntryPoint>> entry_points;
|
||||
std::map<uint32_t, Variable> globals;
|
||||
std::map<uint32_t, Variable> function_vars;
|
||||
|
||||
public:
|
||||
CompilerImpl() {
|
||||
ctx = std::make_unique<llvm::LLVMContext>();
|
||||
module = std::make_unique<llvm::Module>("shader", *ctx);
|
||||
builder = std::make_unique<llvm::IRBuilder<>>(*ctx);
|
||||
}
|
||||
|
||||
bool process_module(std::vector<uint32_t> &spv_module) {
|
||||
spvtools::Context context(SPV_ENV_OPENGL_4_5);
|
||||
auto spv_ctx = spvContextCreate(SPV_ENV_UNIVERSAL_1_0);
|
||||
auto validationResult = spvValidateBinary(spv_ctx, &spv_module[0], spv_module.size(), nullptr);
|
||||
if (validationResult != SPV_SUCCESS) {
|
||||
// TODO: collect diagnostics
|
||||
return false;
|
||||
}
|
||||
|
||||
// process spv_module
|
||||
spvBinaryParse(spv_ctx, this,
|
||||
&spv_module[0], spv_module.size(),
|
||||
impl_parse_header,
|
||||
impl_parse_insn,
|
||||
nullptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
spv_result_t
|
||||
handle_spv_header(spv_endianness_t endianness, uint32_t magic, uint32_t version,
|
||||
uint32_t generator,
|
||||
uint32_t id_bound,
|
||||
uint32_t reserved) {
|
||||
if (version != 0x00010000) {
|
||||
return SPV_ERROR_WRONG_VERSION;
|
||||
}
|
||||
return SPV_SUCCESS;
|
||||
// TODO: allocate structures based on id_bound.
|
||||
}
|
||||
|
||||
std::string format_insn(const spv_parsed_instruction_t* insn) {
|
||||
|
||||
std::stringstream msg_stream;
|
||||
msg_stream << "Opcode(" << insn->opcode << "):";
|
||||
|
||||
for (int i = 0; i < insn->num_operands; i++) {
|
||||
auto operand = &insn->operands[i];
|
||||
msg_stream << " " << std::dec << operand->type << std::hex;
|
||||
for (int j = 0; j < operand->num_words; j++) {
|
||||
msg_stream << (j == 0 ? "[" : ", ") << insn->words[operand->offset + j];
|
||||
}
|
||||
msg_stream << "]";
|
||||
}
|
||||
return msg_stream.str();
|
||||
|
||||
}
|
||||
#pragma clang diagnostic push
|
||||
#pragma ide diagnostic ignored "bugprone-branch-clone"
|
||||
spv_result_t handle_spv_insn(const spv_parsed_instruction_t *insn) {
|
||||
uint32_t rid;
|
||||
uint32_t rty;
|
||||
uint32_t first_operand = insn->num_operands;
|
||||
auto idata = insn->words;
|
||||
|
||||
for (int i = 0; i < insn->num_operands; i++) {
|
||||
auto operand = &insn->operands[i];
|
||||
if (operand->type == SPV_OPERAND_TYPE_RESULT_ID) {
|
||||
rid = OP_WORD(i);
|
||||
} else if (operand->type == SPV_OPERAND_TYPE_TYPE_ID) {
|
||||
rty = OP_WORD(i);
|
||||
} else {
|
||||
first_operand = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
using spv::Op;
|
||||
switch (Op(insn->opcode)) {
|
||||
case Op::OpNop:break;
|
||||
//region Header instructions
|
||||
case Op::OpCapability:
|
||||
switch (spv::Capability(idata[insn->operands[0].offset])) {
|
||||
case spv::Capability::Matrix:
|
||||
case spv::Capability::Float16:
|
||||
case spv::Capability::Float64:
|
||||
case spv::Capability::Shader:
|
||||
// all allowed
|
||||
break;
|
||||
default:
|
||||
return SPV_ERROR_INVALID_CAPABILITY;
|
||||
}
|
||||
break;
|
||||
case Op::OpExtInstImport: {
|
||||
auto id = decode_string_arg(OP_BYTES(1));
|
||||
if (id == "GLSL.std.450") {
|
||||
ext_insts[rid] = SPV_EXT_INST_TYPE_GLSL_STD_450;
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Unknown extinst type " << id;
|
||||
return SPV_ERROR_MISSING_EXTENSION;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Op::OpMemoryModel: {
|
||||
if (spv::AddressingModel(OP_WORD(0)) != spv::AddressingModel::Logical) {
|
||||
BOOST_LOG_TRIVIAL(error) << "Invalid addressing model";
|
||||
return SPV_ERROR_MISSING_EXTENSION;
|
||||
}
|
||||
// We always use memory model GLSL450
|
||||
break;
|
||||
}
|
||||
case Op::OpEntryPoint: {
|
||||
if (spv::ExecutionModel(OP_WORD(0)) != spv::ExecutionModel::Fragment) {
|
||||
break; // simply don't handle it
|
||||
}
|
||||
auto entry = std::make_shared<EntryPoint>(insn);
|
||||
entry_points[entry->name] = std::move(entry);
|
||||
break;
|
||||
}
|
||||
case Op::OpExecutionMode: {
|
||||
// TODO: Save this for when generating the glue function
|
||||
break;
|
||||
}
|
||||
//endregion
|
||||
//region Annotations
|
||||
case Op::OpDecorate: {
|
||||
// TODO: save these for bindings
|
||||
break;
|
||||
}
|
||||
case Op::OpMemberDecorate: {
|
||||
// ignore
|
||||
break;
|
||||
}
|
||||
//endregion
|
||||
//region Types
|
||||
case Op::OpTypeVoid: {
|
||||
types[rid] = std::make_shared<SpirvVoidType>();
|
||||
break;
|
||||
}
|
||||
case Op::OpTypeBool: {
|
||||
types[rid] = std::make_shared<SpirvBoolType>();
|
||||
break;
|
||||
}
|
||||
case Op::OpTypeInt: {
|
||||
types[rid] = std::make_shared<SpirvIntType>(OP_WORD(1), OP_WORD(2) != 0);
|
||||
break;
|
||||
}
|
||||
case Op::OpTypeFloat:
|
||||
types[rid] = std::make_shared<SpirvFloatType>(OP_WORD(1));
|
||||
break;
|
||||
case Op::OpTypeVector: {
|
||||
types[rid] = std::make_shared<SpirvVectorType>(types[OP_WORD(1)], OP_WORD(2));
|
||||
break;
|
||||
}
|
||||
case Op::OpTypeMatrix: {
|
||||
types[rid] = std::make_shared<SpirvMatrixType>(types[OP_WORD(1)], OP_WORD(2));
|
||||
break;
|
||||
}
|
||||
case Op::OpTypeArray: {
|
||||
types[rid] = std::make_shared<SpirvArrayType>(types[OP_WORD(1)], constants[OP_WORD(2)].ival);
|
||||
break;
|
||||
}
|
||||
// Op::OpTypeRuntimeArray: {}
|
||||
case Op::OpTypeStruct: {
|
||||
std::vector<std::shared_ptr<SpirvType>> members;
|
||||
for (int i = 1; i < insn->num_operands; i++) {
|
||||
members.push_back(types[OP_WORD(i)]);
|
||||
}
|
||||
types[rid] = std::make_shared<SpirvStructType>(std::move(members));
|
||||
break;
|
||||
}
|
||||
case Op::OpTypePointer: {
|
||||
// ignore storage class, for now...
|
||||
types[rid] = std::make_shared<SpirvPointerType>(types[OP_WORD(2)]);
|
||||
break;
|
||||
}
|
||||
case Op::OpTypeFunction: {
|
||||
std::vector<std::shared_ptr<SpirvType>> args;
|
||||
for (int i = 2; i < insn->num_operands; i++) {
|
||||
args.push_back(types[OP_WORD(i)]);
|
||||
}
|
||||
types[rid] = std::make_shared<SpirvFunctionType>(types[OP_WORD(1)], std::move(args));
|
||||
break;
|
||||
}
|
||||
//endregion
|
||||
//region Constants
|
||||
case Op::OpConstantTrue: {
|
||||
constants[rid] = Constant(uint32_t(1));
|
||||
break;
|
||||
}
|
||||
case Op::OpConstantFalse:
|
||||
constants[rid] = Constant(uint32_t(0));
|
||||
break;
|
||||
case Op::OpConstant: {
|
||||
auto &type = types[rty];
|
||||
auto ftype = std::dynamic_pointer_cast<SpirvFloatType>(type);
|
||||
auto itype = std::dynamic_pointer_cast<SpirvIntType>(type);
|
||||
if (ftype != nullptr) {
|
||||
double val = 0;
|
||||
float fval = 0;
|
||||
uint64_t bits;
|
||||
switch (ftype->width) {
|
||||
case 64:
|
||||
// we've got a double here...
|
||||
bits = (uint64_t)OP_BYTES(2)[0]
|
||||
| ((uint64_t)(OP_BYTES(2)[1]) << 32);
|
||||
memcpy(&val, &bits, 8);
|
||||
constants[rid] = val;
|
||||
break;
|
||||
case 32:
|
||||
// float
|
||||
memcpy(&fval, OP_BYTES(2), 4);
|
||||
constants[rid] = fval;
|
||||
break;
|
||||
case 16:
|
||||
// half; we don't implement this yet
|
||||
BOOST_LOG_TRIVIAL(warning) << "Half-precision floats incompletely implemented";
|
||||
constants[rid] = fval;
|
||||
break;
|
||||
}
|
||||
} else if (itype != nullptr) {
|
||||
uint64_t val = 0;
|
||||
switch (itype->width) {
|
||||
case 64:
|
||||
constants[rid] = (uint64_t)OP_BYTES(2)[0]
|
||||
| ((uint64_t)(OP_BYTES(2)[1]) << 32);
|
||||
break;
|
||||
case 32:
|
||||
constants[rid] = OP_WORD(2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
//endregion
|
||||
//region Variables
|
||||
case Op::OpVariable: {
|
||||
|
||||
break;
|
||||
}
|
||||
//endregion
|
||||
//region
|
||||
//endregion
|
||||
default:BOOST_LOG_TRIVIAL(info) << "Unhandled instruction " << format_insn(insn);
|
||||
}
|
||||
|
||||
return SPV_SUCCESS;
|
||||
}
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
};
|
||||
|
||||
|
||||
Compiler::Compiler(): impl(std::make_unique<CompilerImpl>()) {
|
||||
}
|
||||
|
||||
bool Compiler::compile(std::vector<uint32_t> &spv_module) {
|
||||
return impl->process_module(spv_module);
|
||||
}
|
||||
|
||||
Compiler::~Compiler() = default;
|
||||
|
||||
spv_result_t
|
||||
impl_parse_header(void *user_data, spv_endianness_t endian, uint32_t magic, uint32_t version, uint32_t generator,
|
||||
uint32_t id_bound, uint32_t reserved) {
|
||||
auto compiler = (CompilerImpl*)user_data;
|
||||
return compiler->handle_spv_header(endian, magic, version, generator, id_bound, reserved);
|
||||
}
|
||||
|
||||
spv_result_t impl_parse_insn(void *user_data, const spv_parsed_instruction_t *parsed_instruction) {
|
||||
auto compiler = (CompilerImpl*)user_data;
|
||||
return compiler->handle_spv_insn(parsed_instruction);
|
||||
}
|
||||
18
src/compiler.hpp
Normal file
18
src/compiler.hpp
Normal file
@@ -0,0 +1,18 @@
|
||||
//
|
||||
// Created by thequux on 5/24/22.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
struct CompilerImpl;
|
||||
class Compiler {
|
||||
std::unique_ptr<CompilerImpl> impl;
|
||||
public:
|
||||
Compiler();
|
||||
|
||||
bool compile(std::vector<uint32_t> &spv_module);
|
||||
virtual ~Compiler();
|
||||
};
|
||||
97
src/pre-compiler.cpp
Normal file
97
src/pre-compiler.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// Created by thequux on 5/23/22.
|
||||
//
|
||||
|
||||
#include <iostream>
|
||||
#include "pre-compiler.hpp"
|
||||
#include <shaderc/shaderc.hpp>
|
||||
#include <spirv-tools/libspirv.hpp>
|
||||
#include <fstream>
|
||||
|
||||
|
||||
std::shared_ptr<std::vector<uint32_t>> pre_compile_shader(const std::string &src) {
|
||||
shaderc::Compiler compiler;
|
||||
shaderc::CompileOptions options;
|
||||
options.SetSourceLanguage(shaderc_source_language_glsl);
|
||||
options.SetOptimizationLevel(shaderc_optimization_level_performance);
|
||||
options.SetTargetSpirv(shaderc_spirv_version_1_0);
|
||||
options.SetTargetEnvironment(
|
||||
shaderc_target_env_opengl_compat,
|
||||
shaderc_env_version::shaderc_env_version_opengl_4_5);
|
||||
|
||||
|
||||
auto result = compiler.CompileGlslToSpv(
|
||||
src,
|
||||
shaderc_glsl_fragment_shader,
|
||||
"shader.frag",
|
||||
"main",
|
||||
options);
|
||||
|
||||
std::string status_message;
|
||||
bool compilation_ok = false;
|
||||
switch (result.GetCompilationStatus()) {
|
||||
case shaderc_compilation_status_success:status_message = "Pre-compile succeeded";
|
||||
compilation_ok = true;
|
||||
break;
|
||||
case shaderc_compilation_status_invalid_stage:status_message = "Failed: Invalid stage";
|
||||
break;
|
||||
case shaderc_compilation_status_compilation_error:status_message = "Failed: Compilation error";
|
||||
break;
|
||||
case shaderc_compilation_status_internal_error:status_message = "Failed: Internal error";
|
||||
break;
|
||||
case shaderc_compilation_status_null_result_object:status_message = "Failed: Null result object";
|
||||
break;
|
||||
case shaderc_compilation_status_invalid_assembly:status_message = "Failed: Invalid assembly";
|
||||
break;
|
||||
case shaderc_compilation_status_validation_error:status_message = "Failed: validation error";
|
||||
break;
|
||||
case shaderc_compilation_status_transformation_error:status_message = "Failed: transformation error";
|
||||
break;
|
||||
case shaderc_compilation_status_configuration_error:status_message = "Failed: configuration error";
|
||||
break;
|
||||
};
|
||||
|
||||
|
||||
auto err_msg = result.GetErrorMessage();
|
||||
if (!err_msg.empty()) {
|
||||
status_message += '\n';
|
||||
status_message += err_msg;
|
||||
}
|
||||
std::cerr << err_msg;
|
||||
|
||||
|
||||
|
||||
if (compilation_ok) {
|
||||
auto module = std::make_shared<std::vector<uint32_t>>(
|
||||
result.cbegin(), result.cend()
|
||||
);
|
||||
|
||||
{
|
||||
std::ofstream shader_color("shader.spv_t");
|
||||
|
||||
spvtools::SpirvTools tools(SPV_ENV_OPENGL_4_5);
|
||||
std::string result;
|
||||
tools.Disassemble(*module, &result,
|
||||
SPV_BINARY_TO_TEXT_OPTION_COLOR |
|
||||
SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES |
|
||||
SPV_BINARY_TO_TEXT_OPTION_INDENT |
|
||||
SPV_BINARY_TO_TEXT_OPTION_COMMENT);
|
||||
shader_color << result;
|
||||
shader_color.flush();
|
||||
shader_color.close();
|
||||
|
||||
std::ofstream shader_spv("shader.spv");
|
||||
result.clear();
|
||||
tools.Disassemble(*module, &result,
|
||||
SPV_BINARY_TO_TEXT_OPTION_INDENT);
|
||||
shader_spv << result;
|
||||
shader_spv.flush();
|
||||
shader_spv.close();
|
||||
|
||||
}
|
||||
|
||||
return module;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
10
src/pre-compiler.hpp
Normal file
10
src/pre-compiler.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
//
|
||||
// Created by thequux on 5/23/22.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include <shaderc/shaderc.hpp>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
std::shared_ptr<std::vector<uint32_t>> pre_compile_shader(const std::string& src);
|
||||
Reference in New Issue
Block a user