Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Review #4

Open
wants to merge 21 commits into
base: empty
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.DS_Store
.idea
cmake-build-debug
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[submodule "external/fmt"]
path = external/fmt
url = https://github.com/fmtlib/fmt.git
[submodule "external/SPIRV-Cross"]
path = external/SPIRV-Cross
url = https://github.com/KhronosGroup/SPIRV-Cross.git
[submodule "external/usse-decoder-gen"]
path = external/usse-decoder-gen
url = https://github.com/Vita3K/usse-decoder-gen.git
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.14)
project(psp2spvc)

set(CMAKE_CXX_STANDARD 17)

add_subdirectory(external)
add_subdirectory(src)
69 changes: 69 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# psp2spvc - An experimental shader compiler for the Playstation Vita.

psp2spvc is a program that translates between the SPIRV and GXP formats.
The goal of the project is to provide an easy, open-source way to generate GXP shaders for the Playstation Vita.

gxmfun shader compatibility:
- [x] clear_f
- [x] clear_v
- [ ] color_f
- [ ] color_v
- [ ] cube_f
- [ ] cube_v

The project is still early into its development, shaders can be unstable and many features are not yet implemented.
If you found an issue or would like to request a feature, feel free to make an [issue](https://github.com/1whatleytay/psp2spvc/issues).

### GLSL
[glslangValidator](https://github.com/KhronosGroup/glslang) is the only GLSL compiler verified to work.
Other GLSL compilers may be supported in the future.

Compile your GLSL as follows:
```shell script
glslangValidator path/to/input.glsl -V -o path/to/output.spv
```
A Vulkan target is recommended.

#### Uniforms
As a workaround to the Vulkan target, uniforms in the default buffer should be defined as follows:
```glsl
uniform export_name { type self; } reference_name;
```
`export_name` is what will be visible in the binary. What will be used for `sceGxmProgramFindParameterByName`.

`reference_name` will be the name used in the rest of the shader.

`type` is the type of the uniform. It can be a struct.

You can refer to the uniform as follows: `reference_name.self`.
OpenGL target/uniform syntax will be provided in the future (minor tweaking is needed).

## Usage
psp2spvc must be provided with one input SPIRV file and one output GXP path.
The path to the input file should be provided as an argument.
The path to the output file must also be provided as argument but must be preceded by `-o`.
```shell script
psp2spvc path/to/input.spv -o path/to/output.gxp
```

### Options
Options can be provided as arguments to modify build or print debug information.

Option | Description
--- | ---
-S | Print shader assembly.
-A | Print allocation messages.
-L | Print other debug messages.
-Oreg-space | Enable register space optimization. Required for larger shaders.

## Building
You need [CMake](https://cmake.org/) to build the project.
The following commands will prepare and build the project on UNIX based systems.
```shell script
git submodule update --init --recursive
mkdir build
cmake ..
make
```

No prebuilt binaries are provided.
2 changes: 2 additions & 0 deletions external/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
add_subdirectory(fmt)
add_subdirectory(SPIRV-Cross)
1 change: 1 addition & 0 deletions external/SPIRV-Cross
Submodule SPIRV-Cross added at 4ce044
1 change: 1 addition & 0 deletions external/fmt
Submodule fmt added at 2aae6b
1 change: 1 addition & 0 deletions external/usse-decoder-gen
Submodule usse-decoder-gen added at 0618cd
87 changes: 87 additions & 0 deletions generate-usse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from yaml import load, Loader
from enum import Enum


class Protection(Enum):
NONE = 0
SAFE = 1
DEBUG = 2


protection = Protection.DEBUG

bit_types = """
typedef uint64_t Instruction;
typedef uint64_t Param;
"""

debug = '#include <stdexcept>\n\n' if protection == Protection.DEBUG else ''

with open('external/usse-decoder-gen/grammar.yaml', 'r') as stream:
instructions = load(stream, Loader=Loader)

header = '#pragma once\n\n#include <cstdint>\n\nnamespace usse {' + bit_types + '\n'
source = '#include <gxp/instructions.h>\n\n' + debug + 'namespace usse {\n'

for instruction_name, instruction in instructions.items():
members = instruction['members']
first = True
declaration = 'Instruction make' + instruction_name + '('
parameters = ''
function = '\t\tInstruction inst = 0;\n'
index = 64
for member in members:
member_name = list(member)[0]
member_info = member[member_name]
if type(member_info) is str:
index -= len(member_info)
function += '\t\tinst |= 0b' + member_info + 'ull << ' + str(index) + 'u;\n'
elif type(member_info) is int:
index -= member_info
if not first:
parameters += ',\n\t\t'
parameters += 'Param/*' + str(member_info) + '*/ ' + member_name
first = False
if protection == Protection.DEBUG:
function += '\t\tif ((' + member_name + ' & ~0b' + ('1' * member_info) + 'ull) != 0)\n'\
+ '\t\t\tthrow std::runtime_error("Instruction field ' + member_name\
+ ' for ' + instruction_name + ' out of bounds.' + '");\n'
if protection == Protection.NONE:
function += '\t\tinst |= ' + member_name + ' << ' + str(index) + 'u;\n'
else:
function += '\t\tinst |= (' + member_name + ' & 0b'\
+ ('1' * member_info) + 'ull) << ' + str(index) + 'u;\n'
else:
if 'offset' in member_info:
index = member_info['offset']
else:
index -= member_info['size']
if 'match' in member_info:
function += '\t\tinst |= 0b' + member_info['match'] + 'ull << ' + str(index) + 'u;\n'
else:
if not first:
parameters += ',\n\t\t'
parameters += 'Param/*' + str(member_info['size']) + '*/ ' + member_name
first = False
if protection == Protection.DEBUG:
function += '\t\tif ((' + member_name + ' & ~0b' + ('1' * member_info['size']) + 'ull) != 0)\n'\
+ '\t\t\tthrow std::runtime_error("Instruction field ' + member_name \
+ ' for ' + instruction_name + ' out of bounds.' + '");\n'
if protection == Protection.NONE:
function += '\t\tinst |= ' + member_name + ' << ' + str(index) + 'u;\n'
else:
function += '\t\tinst |= (' + member_name + ' & 0b'\
+ ('1' * member_info['size']) + 'ull) << ' + str(index) + 'u;\n'

if parameters:
declaration += '\n\t\t' + parameters
declaration += ')'
header += '\t' + declaration + ';\n\n'
source += '\t' + declaration + ' {\n' + function + '\t\treturn inst;\n\t}\n\n'

header += '}\n'
source += '}\n'
with open('src/gxp/include/gxp/instructions.h', 'w+') as header_out:
header_out.write(header)
with open('src/gxp/src/instructions.cpp', 'w+') as source_out:
source_out.write(source)
8 changes: 8 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
add_subdirectory(util)
add_subdirectory(gxp)
add_subdirectory(translator)
add_subdirectory(interface)

add_executable(psp2spvc
main.cpp)
target_link_libraries(psp2spvc util interface)
16 changes: 16 additions & 0 deletions src/gxp/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
add_library(gxp
include/gxp/usse.h
include/gxp/instructions.h
include/gxp/disasm.h
include/gxp/gxp.h
include/gxp/block.h
include/gxp/builder.h

src/usse.cpp
src/instructions.cpp
src/disasm.cpp
src/gxp.cpp
src/builder.cpp
src/block.cpp)
target_include_directories(gxp PUBLIC include)
target_link_libraries(gxp PUBLIC util)
67 changes: 67 additions & 0 deletions src/gxp/include/gxp/block.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once

#include <gxp/usse.h>

#include <vector>

namespace gxp {
class Builder;

class Block {
std::vector<usse::Instruction> instructions;

void printDisassembly(const std::string &name,
const std::vector<usse::RegisterReference> &sources,
const usse::RegisterReference *destination = nullptr);

explicit Block(Builder &parent);

friend class gxp::Builder;
public:
Builder &parent;

void moveData(
usse::RegisterReference source,
usse::RegisterReference destination);

void createMov(
usse::RegisterReference source,
usse::RegisterReference destination);
void createPack(
usse::RegisterReference source,
usse::RegisterReference destination);
void createDot(
usse::RegisterReference first,
usse::RegisterReference second,
usse::RegisterReference destination);
void createAdd(
usse::RegisterReference first,
usse::RegisterReference second,
usse::RegisterReference destination);
void createSub(
usse::RegisterReference first,
usse::RegisterReference second,
usse::RegisterReference destination);
void createMul(
usse::RegisterReference first,
usse::RegisterReference second,
usse::RegisterReference destination);
void createExp(
usse::RegisterReference source,
usse::RegisterReference destination);
void createLog(
usse::RegisterReference source,
usse::RegisterReference destination);
void createReverseSquareRoot(
usse::RegisterReference source,
usse::RegisterReference destination);
void createMin(
usse::RegisterReference first,
usse::RegisterReference second,
usse::RegisterReference destination);
void createMax(
usse::RegisterReference first,
usse::RegisterReference second,
usse::RegisterReference destination);
};
}
87 changes: 87 additions & 0 deletions src/gxp/include/gxp/builder.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#pragma once

#include <gxp/gxp.h>
#include <gxp/block.h>

#include <map>
#include <string>

namespace gxp {
typedef uint64_t Instruction;

enum class ShaderType : uint8_t {
Vertex = 0,
Fragment = 1,
};

// Should be 64 (some instructions only have 6 bit index), at most 128. Set higher for non -Oreg-space shaders.
constexpr size_t maxTemporaryRegisters = 128;

class Parameter {
public:
std::string name;
ParameterCategory category = ParameterCategory::Uniform;
ParameterSemantic semantic = ParameterSemantic::None;

usse::DataType type;

// Will be filled out by Builder.
uint32_t resourceIndex = 0;
uint32_t containerIndex = 0;

usse::RegisterBank getBank();
};

class BuilderConfig {
public:
bool printDisassembly = false;
bool printAllocations = false;
};

class Builder {
BuilderConfig config;

ProgramHeader header;
ProgramVaryings varyings;

uint32_t paRegPointer = 0;
uint32_t saRegPointer = 0;
uint32_t oRegPointer = 0;
uint32_t iRegPointer = 0;

std::array<bool, maxTemporaryRegisters> tRegSpace = { };
uint32_t tMaxRegs = 0;

std::vector<std::unique_ptr<Block>> primaryBlocks;
std::vector<std::unique_ptr<Block>> secondaryBlocks;
std::vector<Parameter> parameters;
std::vector<float> literals;
std::vector<ProgramFragmentInputInfo> fragmentInputs;

friend class Block;
public:
void setType(ShaderType type);
ShaderType getType();

Block *createPrimaryBlock();
Block *createSecondaryBlock();

usse::RegisterReference allocateRegister(usse::RegisterBank bank, usse::DataType type);
void freeRegister(usse::RegisterReference reg);

usse::RegisterReference registerParameter(const Parameter &parameter);
usse::RegisterReference registerLiteral(const std::vector<float> &literal);

std::map<ProgramVarying, usse::RegisterReference> registerVertexVaryings(
const std::vector<ProgramVarying> &outputs, const std::vector<ProgramVectorInfo> &texCoords);
std::map<ProgramVarying, usse::RegisterReference> registerFragmentVaryings(
const std::vector<ProgramVectorInfo> &inputs /*, samplers...*/);

usse::RegisterReference createFragmentOutput(usse::Type type, uint32_t components);

std::vector<uint8_t> build();

Builder();
explicit Builder(BuilderConfig config);
};
}
12 changes: 12 additions & 0 deletions src/gxp/include/gxp/disasm.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <gxp/usse.h>

#include <optional>

namespace usse::disasm {
std::string disassembleReference(usse::RegisterReference reference);
std::string disassemble(const std::string &name,
const std::vector<usse::RegisterReference> &sources,
const usse::RegisterReference *destination = nullptr);
}
Loading