Skip to content
Closed
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
6 changes: 4 additions & 2 deletions .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true

# Somehow the binary .rgba file is failing the final newline check.
[*.rgba]
# Somehow some binary files are failing checks.
[*.{rgba,pbj}]
end_of_line = unset
insert_final_newline = unset
trim_trailing_whitespace = unset

# Files generated from Crowdin may include trailing whitespace.
[*.ftl]
Expand Down
76 changes: 76 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ members = [
"render",
"render/canvas",
"render/naga-agal",
"render/pbasm",
"render/pbasm/integration_tests",
"render/wgpu",
"render/webgl",

Expand Down
4 changes: 4 additions & 0 deletions render/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ num-derive = { workspace = true }
byteorder = { workspace = true }
wgpu = { workspace = true, optional = true }
indexmap = { workspace = true }
pest = { version = "2.8.1", optional = true }
pest_derive = { version = "2.8.1", optional = true }
serde_json = { version = "1.0.140", optional = true }

# This crate has a `compile_error!` on apple platforms
[target.'cfg(not(target_vendor = "apple"))'.dependencies.renderdoc]
Expand All @@ -50,3 +53,4 @@ tessellator = ["lyon"]
web = ["wasm-bindgen"]
wgpu = ["dep:wgpu"]
serde = ["dep:serde"]
assembly = ["dep:pest", "dep:pest_derive", "dep:serde_json"]
17 changes: 17 additions & 0 deletions render/pbasm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "pbasm"
description = "pbasm: The Last PixelBender (Assembler)"
authors = ["Kamil Jarosz <kjarosh256@gmail.com>"]
version = "0.1.0"
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true

[lints]
workspace = true

[dependencies]
clap = { workspace = true }
anyhow = { workspace = true }
ruffle_render = { path = "..", features = ["assembly"] }
27 changes: 27 additions & 0 deletions render/pbasm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# pbasm: The Last PixelBender (Assembler)

`pbasm` is a PixelBender assembler with a custom syntax.

## Usage

In order to assemble a `.pbasm` file, run:

```bash
pbasm kernel.pbasm -o kernel.pbj
```

In order to disassemble a `.pbj` file, run:

```bash
pbasm -d kernel.pbj -o kernel.pbasm
```

## Syntax

See [integration tests](./integration_tests/) for syntax examples.

## Notes

`pbasm` aims mainly to be useful for Ruffle developers, which means that it
should be able to produce malformed shaders too, so that we can make sure they
are handled properly.
26 changes: 26 additions & 0 deletions render/pbasm/integration_tests/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[package]
name = "pbasm_integration_tests"
authors.workspace = true
edition.workspace = true
homepage.workspace = true
license.workspace = true
repository.workspace = true
version.workspace = true

[lints]
workspace = true

[dev-dependencies]
pbasm = { path = ".." }
ruffle_fs_tests_runner = { path = "../../../tests/fs-tests-runner" }
libtest-mimic = { workspace = true }
serde = { workspace = true, features = ["derive"] }
toml = { workspace = true }
anyhow = { workspace = true }
clap = { workspace = true }
vfs = { workspace = true }

[[test]]
name = "integration_tests"
harness = false
path = "src/runner.rs"
13 changes: 13 additions & 0 deletions render/pbasm/integration_tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# pbasm Integration Tests

This directory contains integration tests for `pbasm`.
Each directory with `test.toml` contains one integration test.

## `test.toml`

```toml
# Type of the test, either 'assembly', 'disassembly', or 'roundtrip'.
type = "roundtrip"
# If set to true, the test will be ignored.
ignore = false
```
152 changes: 152 additions & 0 deletions render/pbasm/integration_tests/src/runner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//! Integration tests runner for pbasm.

use anyhow::{anyhow, Context, Result};
use libtest_mimic::Trial;
use pbasm::{run_main, Opt};
use ruffle_fs_tests_runner::{FsTestsRunner, TestLoaderParams};
use serde::Deserialize;
use std::path::Path;
use std::{borrow::Cow, path::PathBuf};
use vfs::VfsPath;

const TEST_TOML_NAME: &str = "test.toml";

#[derive(Clone, Copy, Deserialize)]
enum TestType {
Roundtrip,
Assemble,
Dissassemble,
}

impl TestType {
fn perform_assembly(self) -> bool {
matches!(self, TestType::Assemble | TestType::Roundtrip)
}

fn perform_disassembly(self) -> bool {
matches!(self, TestType::Dissassemble | TestType::Roundtrip)
}
}

#[derive(Clone, Deserialize)]
#[serde(default, deny_unknown_fields)]
struct TestOptions {
pub r#type: TestType,
pub ignore: bool,
pub pbj_path: String,
pub pbasm_path: String,
}

impl Default for TestOptions {
fn default() -> Self {
Self {
r#type: TestType::Roundtrip,
ignore: false,
pbj_path: "test.pbj".to_owned(),
pbasm_path: "test.pbasm".to_owned(),
}
}
}

impl TestOptions {
fn read(path: &VfsPath) -> Result<Self> {
let result = toml::from_str(&path.read_to_string()?)?;
Ok(result)
}

fn pbj_path(&self, test_dir: &VfsPath) -> Result<VfsPath> {
test_dir
.join(&self.pbj_path)
.context("Failed to get pbj path")
}

fn pbasm_path(&self, test_dir: &VfsPath) -> Result<VfsPath> {
test_dir
.join(&self.pbasm_path)
.context("Failed to get pbasm path")
}
}

fn main() {
let mut runner = FsTestsRunner::new();

runner
.with_descriptor_name(Cow::Borrowed(TEST_TOML_NAME))
.with_test_loader(Box::new(|params| Some(load_test(params))));

runner.run()
}

fn load_test(params: TestLoaderParams) -> Trial {
let test_dir = params.test_dir.clone();
let test_dir_real = params.test_dir_real.into_owned();
let name = params.test_name;

let descriptor_path = test_dir.join("test.toml").unwrap();

let options = TestOptions::read(&descriptor_path)
.map_err(|e| anyhow!("Failed to parse {}: {e}", descriptor_path.as_str()))
.expect("Failed to parse test descriptor");
let ignore = options.ignore;

let mut trial = Trial::test(name.to_string(), move || {
let pbj_path = options.pbj_path(&test_dir)?;
let pbj_path_real = to_real_path(&test_dir_real, &pbj_path);
let pbasm_path = options.pbasm_path(&test_dir)?;
let pbasm_path_real = to_real_path(&test_dir_real, &pbasm_path);

let pbj_actual_path = test_dir_real.join("actual.pbj");
let pbasm_actual_path = test_dir_real.join("actual.pbasm");

if options.r#type.perform_assembly() {
let opt = Opt {
source: pbasm_path_real.to_str().unwrap().to_string(),
disassemble: false,
output: Some(pbj_actual_path.to_str().unwrap().to_string()),
};
run_test(opt, &pbj_path_real)?;
}

if options.r#type.perform_disassembly() {
let opt = Opt {
source: pbj_path_real.to_str().unwrap().to_string(),
disassemble: true,
output: Some(pbasm_actual_path.to_str().unwrap().to_string()),
};
run_test(opt, &pbasm_path_real)?;
}

Ok(())
});
if ignore {
trial = trial.with_ignored_flag(true);
}
trial
}

fn to_real_path(real_dir: &Path, file: &VfsPath) -> PathBuf {
real_dir.join(file.as_str().strip_prefix('/').unwrap())
}

fn run_test(opt: Opt, expected_path: &Path) -> Result<()> {
let actual_path = opt.output.clone().unwrap();
run_main(opt).map_err(|e: anyhow::Error| anyhow!("Failed to execute pbasm: {e}"))?;

let actual = std::fs::read(&actual_path)?;
let expected = std::fs::read(expected_path).map_err(|e| {
anyhow!(
"Error reading test file {}: {e}",
expected_path.to_string_lossy()
)
})?;

if actual != expected {
return Err(anyhow!(
"Test failed: Output doesn't match: {}",
actual_path.to_string()
));
}

let _ = std::fs::remove_file(actual_path);
Ok(())
}
2 changes: 2 additions & 0 deletions render/pbasm/integration_tests/tests/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
actual.pbj
actual.pbasm
Loading
Loading