Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: pmmp/ext-chunkutils2
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.3.4
Choose a base ref
...
head repository: pmmp/ext-chunkutils2
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
  • 19 commits
  • 10 files changed
  • 2 contributors

Commits on Feb 26, 2023

  1. 0.3.5 is next

    dktapps committed Feb 26, 2023
    Copy the full SHA
    e2d6606 View commit details

Commits on Feb 28, 2023

  1. Significantly improve performance of PalettedBlockArray::validate() (#35

    )
    
    This change introduces a fast path for validation using carry-out vectors, as discussed here: https://devblogs.microsoft.com/oldnewthing/20190301-00/?p=101076
    
    TL;DR: It's possible to detect invalid palette offsets by subtracting the current "word" from a bitfield composed of the max valid palette offset, which is significantly faster than shifting and naively comparing each one.
    
    In addition, we allow the code to chew through the whole loop even if invalid offsets are detected, in the hope that compilers will use SIMD instructions and/or parallelize the loop, which is also substantially faster than breaking out early on a branch condition to throw an exception.
    
    The downside of the fast path is that it cannot report the exact offset an error occurred at, only that an error is present. For this reason, the old, slower code is retained, which allows generating detailed errors if an error is detected.
    
    Note: This does cause a change of behaviour for palettes with padded words (3, 5, and 6 bits per block). The palette is now validated to be zero as a side effect of this change. If non-zero padding is found, the fast path will fail, and fallback to the slow path, which will ignore the error. We may want to explicitly ignore or force padding to be zero.
    dktapps authored Feb 28, 2023
    Copy the full SHA
    746cdfa View commit details
  2. Update ci.yml

    dktapps authored Feb 28, 2023
    Copy the full SHA
    5c421d4 View commit details

Commits on Mar 13, 2023

  1. Release 0.3.5

    dktapps committed Mar 13, 2023
    Copy the full SHA
    036b4af View commit details
  2. 0.3.6 is next

    dktapps committed Mar 13, 2023
    Copy the full SHA
    eebb253 View commit details

Commits on Nov 15, 2023

  1. Bump actions/checkout from 3 to 4 (#37)

    Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
    - [Release notes](https://github.com/actions/checkout/releases)
    - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
    - [Commits](actions/checkout@v3...v4)
    
    ---
    updated-dependencies:
    - dependency-name: actions/checkout
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Nov 15, 2023
    Copy the full SHA
    e80ab43 View commit details

Commits on Nov 24, 2023

  1. actions: update PHP versions

    dktapps authored Nov 24, 2023
    Copy the full SHA
    4e5ace8 View commit details
  2. Copy the full SHA
    5fa61b5 View commit details
  3. wtf

    dktapps authored Nov 24, 2023
    Copy the full SHA
    b1b0373 View commit details
  4. Copy the full SHA
    58edf80 View commit details

Commits on Jan 3, 2024

  1. Bump actions/upload-artifact from 3 to 4 (#39)

    Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
    - [Release notes](https://github.com/actions/upload-artifact/releases)
    - [Commits](actions/upload-artifact@v3...v4)
    
    ---
    updated-dependencies:
    - dependency-name: actions/upload-artifact
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Jan 3, 2024
    Copy the full SHA
    c903f19 View commit details

Commits on Feb 2, 2024

  1. Bump actions/cache from 3 to 4 (#40)

    Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
    - [Release notes](https://github.com/actions/cache/releases)
    - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
    - [Commits](actions/cache@v3...v4)
    
    ---
    updated-dependencies:
    - dependency-name: actions/cache
      dependency-type: direct:production
      update-type: version-update:semver-major
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
    dependabot[bot] authored Feb 2, 2024
    Copy the full SHA
    baccf98 View commit details

Commits on Dec 17, 2024

  1. Fix test result uploading

    dktapps authored Dec 17, 2024
    Copy the full SHA
    13e9d28 View commit details
  2. Specialize palette impl for 6, 8 and 16 bpb

    Instead of wasting a bunch of memory and CPU time,
    use vectors to save memory on larger sizes, and
    a map to lookup IDs instead of bruteforce searching
    them. 16 bpb benefits a crazy amount by this, with
    write operations becoming 100x faster. 8 bpb gets 20x
    faster, and to my surprise, 6 bpb gets 4x faster.
    dktapps committed Dec 17, 2024
    Copy the full SHA
    c3e4ea7 View commit details
  3. Copy the full SHA
    343a1d9 View commit details

Commits on Dec 18, 2024

  1. Added PalettedBlockArray::setPalette()

    this is useful for conversion operations, e.g. mapping the palette.
    
    closes #28
    dktapps committed Dec 18, 2024
    Copy the full SHA
    da8ea72 View commit details
  2. ...

    dktapps committed Dec 18, 2024
    Copy the full SHA
    d5ab6e7 View commit details
  3. tidy

    dktapps committed Dec 18, 2024
    Copy the full SHA
    87ed055 View commit details
  4. Copy the full SHA
    ed87a57 View commit details
60 changes: 40 additions & 20 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -7,21 +7,25 @@ on:

jobs:
build:
name: PHP ${{ matrix.php }} (ZTS) Valgrind ${{ matrix.valgrind }}
runs-on: ubuntu-18.04
runs-on: ubuntu-20.04
if: "!contains(github.event.head_commit.message, '[ci skip]')"

name: Tests (PHP ${{ matrix.php }}, Valgrind ${{ matrix.valgrind }}, Debug=${{ matrix.debug }}, ZTS=${{ matrix.zts }})
strategy:
fail-fast: false
matrix:
php: ['8.0.27', '8.1.14', '8.2.1']
php:
- 8.1.26
- 8.2.13
- 8.3.0
valgrind: [0, 1]
debug: [enable, disable]
zts: [enable, disable]

env:
CFLAGS: "-march=x86-64"
CXXFLAGS: "-march=x86-64"
CFLAGS: "-march=x86-64 -ftree-vectorize -fopt-info-vec"
CXXFLAGS: "-march=x86-64 -ftree-vectorize -fopt-info-vec"
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Install Valgrind
if: matrix.valgrind == '1'
@@ -31,25 +35,41 @@ jobs:
echo "PHP_BUILD_CONFIGURE_OPTS=--with-valgrind" >> $GITHUB_ENV
- name: Restore PHP build cache
uses: actions/cache@v4
id: php-build-cache
uses: actions/cache@v3
with:
path: ${{ github.workspace }}/php
key: php-build-debug-${{ matrix.php }}-valgrind-${{ matrix.valgrind }}
path: ${{ github.workspace }}/php
key: php-${{ matrix.php }}-debug-${{ matrix.debug }}-valgrind-${{ matrix.valgrind }}-zts-${{ matrix.zts }}

- name: Clone php-build repository
- name: Install PHP build dependencies
if: steps.php-build-cache.outputs.cache-hit != 'true'
uses: actions/checkout@v3
with:
repository: pmmp/php-build
path: php-build
run: |
sudo apt-get update && sudo apt-get install \
re2c
- name: Get number of CPU cores
if: steps.php-build-cache.outputs.cache-hit != 'true'
uses: SimenB/github-actions-cpu-cores@v2
id: cpu-cores

- name: Download PHP
if: steps.php-build-cache.outputs.cache-hit != 'true'
working-directory: /tmp
run: curl -L https://github.com/php/php-src/archive/refs/tags/php-${{ matrix.php }}.tar.gz | tar -xz

- name: Compile PHP
if: steps.php-build-cache.outputs.cache-hit != 'true'
working-directory: /tmp/php-src-php-${{ matrix.php }}
run: |
cd $GITHUB_WORKSPACE/php-build
./install-dependencies.sh
PHP_BUILD_ZTS_ENABLE=on PHP_BUILD_CONFIGURE_OPTS="$PHP_BUILD_CONFIGURE_OPTS --enable-debug" ./bin/php-build ${{ matrix.php }} $GITHUB_WORKSPACE/php
./buildconf --force
./configure \
--disable-all \
--enable-cli \
--${{ matrix.zts }}-zts \
--${{ matrix.debug}}-debug \
"$PHP_BUILD_CONFIGURE_OPTS" \
--prefix="${{ github.workspace }}/php"
make -j ${{ steps.cpu-cores.outputs.count }} install
- name: Dump PHP info
run: $GITHUB_WORKSPACE/php/bin/php -i
@@ -65,9 +85,9 @@ jobs:

- name: Upload test results
if: failure()
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.php }}-valgrind-${{ matrix.valgrind }}
name: test-results-${{ matrix.php }}-debug-${{ matrix.debug }}-valgrind-${{ matrix.valgrind }}-zts-${{ matrix.zts }}
path: |
${{ github.workspace }}/tests/*
!${{ github.workspace }}/tests/*.phpt
2 changes: 1 addition & 1 deletion config.w32
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ ARG_ENABLE("chunkutils2", "Enable PocketMine ChunkUtils2 extension", "no");

if (PHP_CHUNKUTILS2 != "no") {
EXTENSION("chunkutils2", "chunkutils2.cpp", PHP_CHUNKUTILS2_SHARED, "/DZEND_ENABLE_STATIC_TSRMLS_CACHE=1 /permissive- /I" + configure_module_dirname + " /I" + configure_module_dirname + "/gsl/include /DGSL_THROW_ON_CONTRACT_VIOLATION=1");
ADD_FLAG("CFLAGS_CHUNKUTILS2", "/EHsc");
ADD_FLAG("CFLAGS_CHUNKUTILS2", "/EHsc /Qvec-report:1");
ADD_SOURCES(
configure_module_dirname + "/src",
"PhpLightArray.cpp PhpPalettedBlockArray.cpp PhpSubChunkConverter.cpp",
4 changes: 4 additions & 0 deletions lib/BlockArrayContainer.h
Original file line number Diff line number Diff line change
@@ -108,6 +108,10 @@ class BlockArrayContainer {
return blockArray->getPalette();
}

void setPalette(const gsl::span<Block>& newPalette) {
blockArray->setPalette(newPalette);
}

unsigned short getMaxPaletteSize() const {
return blockArray->getMaxPaletteSize();
}
162 changes: 162 additions & 0 deletions lib/Palette.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#ifndef HAVE_PALETTE_H
#define HAVE_PALETTE_H

template <size_t MAX_PALETTE_SIZE>
class PaletteUtils final {
public:
static void checkSize(size_t size) {
if (size > MAX_PALETTE_SIZE) {
throw std::length_error("palette size should be at most " + std::to_string(MAX_PALETTE_SIZE) + " entries, but received " + std::to_string(size) + " entries");
}
if (size == 0) {
throw std::length_error("palette cannot have a zero size");
}
}
};

/*
* Small palettes use fixed arrays to avoid vector indirections and allocations
* Performance is more important here because the amount of wasted memory is insignificant
* regardless of the number of elements in the palette.
*
* Lookups are done with a linear search, which is faster than using a hash map for small
* numbers of elements.
*/
template <size_t MAX_PALETTE_SIZE, typename Block>
class SmallPalette final {
private:
std::array<Block, MAX_PALETTE_SIZE> palette;
unsigned short nextPaletteIndex = 0;

void initFromData(const gsl::span<const Block> paletteEntries) {
PaletteUtils<MAX_PALETTE_SIZE>::checkSize(paletteEntries.size());

memcpy(palette.data(), paletteEntries.data(), paletteEntries.size() * sizeof(Block));
nextPaletteIndex = (unsigned short)paletteEntries.size();
}
public:
SmallPalette(Block block) {
palette[nextPaletteIndex++] = block;
}

SmallPalette(const std::vector<Block>& paletteEntries) {
initFromData(paletteEntries);
}

SmallPalette(const gsl::span<const Block>& paletteEntries) {
initFromData(paletteEntries);
}

SmallPalette(const SmallPalette& otherArray) {
memcpy(palette.data(), otherArray.palette.data(), sizeof(palette));
nextPaletteIndex = otherArray.nextPaletteIndex;
}

Block get(unsigned int offset) const {
return palette[offset];
}

void set(unsigned int offset, Block val) {
palette[offset] = val;
}

const gsl::span<const Block> getPalette() const {
return gsl::span<const Block>(palette.data(), nextPaletteIndex);
}

size_t size() const {
return nextPaletteIndex;
}

int addOrLookup(Block val) {
for (int offset = 0; offset < nextPaletteIndex; ++offset) {
if (palette[offset] == val) {
return offset;
}
}

if (nextPaletteIndex >= MAX_PALETTE_SIZE) {
return -1;
}
int offset = nextPaletteIndex++;
palette[offset] = val;
return offset;
}
};

/*
* For large numbers of elements, it's common for a significant fraction of the palette's
* capacity to be unused, so using a fixed array like SmallPalette would waste a lot of
* memory. We use a vector instead and pay a small performance penalty.
*
* We use the memory we saved to instead keep a hash map of block -> offset for lookups,
* which significantly improves performance for large palettes. (We only technically save
* memory as long as the palette is less than half (?) full, but the performance benefits
* remain in any case.)
*/
template <size_t MAX_PALETTE_SIZE, typename Block>
class LargePalette final {
private:
std::vector<Block> palette;
std::unordered_map<Block, unsigned int> blockToOffset;

void initFromData(const gsl::span<const Block> paletteEntries) {
PaletteUtils<MAX_PALETTE_SIZE>::checkSize(paletteEntries.size());

palette = std::vector<Block>(paletteEntries.begin(), paletteEntries.end());
for (unsigned int i = 0; i < palette.size(); ++i) {
blockToOffset[palette[i]] = i;
}
}

public:
LargePalette(Block block) {
palette.push_back(block);
blockToOffset[block] = 0;
}

LargePalette(const std::vector<Block>& paletteEntries) {
initFromData(paletteEntries);
}

LargePalette(const gsl::span<const Block>& paletteEntries) {
initFromData(paletteEntries);
}

LargePalette(const LargePalette& otherArray) {
palette = otherArray.palette;
blockToOffset = otherArray.blockToOffset;
}

Block get(unsigned int offset) const {
return palette[offset];
}

void set(unsigned int offset, Block val) {
palette[offset] = val;
}

const gsl::span<const Block> getPalette() const {
return gsl::span<const Block>(palette.data(), palette.size());
}

size_t size() const {
return palette.size();
}

int addOrLookup(Block val) {
auto it = blockToOffset.find(val);
if (it != blockToOffset.end()) {
return it->second;
}

if (palette.size() >= MAX_PALETTE_SIZE) {
return -1;
}
int offset = palette.size();
palette.push_back(val);
blockToOffset[val] = offset;
return offset;
}
};
#endif
Loading