Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
68905dc
vtfpp: maintain byte order of underlying buffers
partyvan Sep 9, 2025
07aaa98
vtfpp: ImageConversion: disambiguate LERep conversions
partyvan Sep 10, 2025
91bcd67
sourcepp: adopt LERep from vtfpp
partyvan Sep 11, 2025
986a5aa
sourcepp: Bits: ergonomics+docs
partyvan Sep 12, 2025
ae0e619
docs: let doxygen know about some alias-declaring macros
partyvan Sep 12, 2025
798731e
feat(ext): allow exotic architectures to provide their own compresson…
partyvan Sep 12, 2025
7ab8776
sourcepp: more LERep constructors + convenient read
partyvan Sep 12, 2025
6206f0d
vtfpp: start using LERep where applicable
partyvan Sep 12, 2025
bb5d864
Merge branch 'main' into vtfpp-endian
partyvan Sep 12, 2025
db8764c
vtfpp: ImagePixelV2
partyvan Sep 14, 2025
1f4cf9b
vtfpp: low-hanging fruit for ImagePixelV2
partyvan Sep 14, 2025
25e5282
sourcepp: add SOURCEPP_REVERSE to macros and clean up helper names
partyvan Sep 14, 2025
2495698
vtfpp: ImagePixelV2: reverse offsets in BITS declarations (oopsie)
partyvan Sep 14, 2025
9694ad5
vtfpp: ImagePixelV2: make BITS pixel formats use LERep!
partyvan Sep 14, 2025
a82ddfd
fix(cmake): enable compliant preprocessor on msvc
partyvan Sep 14, 2025
ad37b2d
vtfpp: ImagePixelV2: constexpr correctness
partyvan Sep 14, 2025
a761f0c
sourcepp: reinterpret_le -> deref_as_le
partyvan Sep 14, 2025
d83a760
vtfpp: account for byte order sensitivity in compressonator
partyvan Sep 14, 2025
e816e6b
vtfpp: use f32le when cubemapping
partyvan Sep 14, 2025
e0e722f
vtfpp: ImagePixelV2: more conster more gooder
partyvan Sep 14, 2025
f5bde62
vtfpp: more ImagePixelV2
partyvan Sep 14, 2025
4688d22
vtfpp: all usage of ImagePixel replaced by ImagePixelV2
partyvan Sep 14, 2025
a530687
vtfpp: ImagePixelV2 simple declarations: store LERep; callable as bas…
partyvan Sep 15, 2025
8cbcea6
vtfpp: add image format tests
partyvan Sep 15, 2025
7ae949e
sourcepp: why was this public in the first place
partyvan Sep 16, 2025
1d9a33e
vtfpp: fix qoi parse on BE
partyvan Sep 16, 2025
7ba4aea
vtfpp: lift execution conditionals to a dedicated macro; deduplicate …
partyvan Sep 16, 2025
2e9f78b
vtfpp: make 16 bit stb reads portable
partyvan Sep 16, 2025
45fa7a1
vtfpp: add vtf read order checks to tests as well
partyvan Sep 16, 2025
2d300ff
vtfpp: give extfmt_img tests corresponding roundtrip attempts
partyvan Sep 16, 2025
5d03779
vtfpp: back to native during resize
partyvan Sep 16, 2025
72083c9
vtfpp: compressed write tests
partyvan Sep 17, 2025
c58f776
Merge remote-tracking branch 'upstream/main' into vtfpp-endian
partyvan Sep 18, 2025
fcdd59c
fix(ext): cmake indentation
partyvan Sep 18, 2025
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
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ project(sourcepp
DESCRIPTION "Several modern C++20 libraries for sanely parsing Valve formats."
HOMEPAGE_URL "https://craftablescience.info/sourcepp")
set(CMAKE_CXX_STANDARD 20)
if(MSVC)
# secret second flag to *actually* make msvc a c++20-compilant compiler
add_compile_options($<$<COMPILE_LANGUAGE:CXX>:/Zc:preprocessor>)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cmake/AddPrettyParser.cmake does this too, is this necessary to pull to a larger scope?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think it was applying in all the places ImageConversion.h was getting included, but i can double check

endif()
set(CMAKE_CXX_STANDARD_REQUIRED ON)


Expand Down
4 changes: 4 additions & 0 deletions Doxyfile
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,10 @@ PREDEFINED =
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.

EXPAND_AS_DEFINED =
EXPAND_AS_DEFINED += SOURCEPP_LEREP_DEFINE
EXPAND_AS_DEFINED += SOURCEPP_LEREP_DEFINE_P
EXPAND_AS_DEFINED += SOURCEPP_MAT_DEFINE
EXPAND_AS_DEFINED += SOURCEPP_VEC_DEFINE

# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
Expand Down
2 changes: 1 addition & 1 deletion bench/vtfpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace {
VTFLib::CVTFFile vtf; \
vtf.Load(ASSET_ROOT "vtfpp/fmt/" #Format ".vtf"); \
for ([[maybe_unused]] auto _: state) { \
std::vector<std::byte> data(vtf.GetWidth() * vtf.GetHeight() * sizeof(ImagePixel::RGBA8888)); \
std::vector<std::byte> data(vtf.GetWidth() * vtf.GetHeight() * sizeof(ImagePixelV2::RGBA8888)); \
benchmark::DoNotOptimize(VTFLib::CVTFFile::ConvertToRGBA8888(vtf.GetData(0, 0, 0, 0), reinterpret_cast<vlByte*>(data.data()), vtf.GetWidth(), vtf.GetHeight(), IMAGE_FORMAT_##VTFLibFormat)); \
} \
} \
Expand Down
13 changes: 8 additions & 5 deletions ext/compressonator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ function(target_link_compressonator TARGET)
"${COMPRESSONATOR_DIR}/lib/macOS_arm64/libCMP_Core$<$<CONFIG:Debug>:d>.a")
elseif(UNIX)
target_link_libraries(${TARGET} PRIVATE
"${COMPRESSONATOR_DIR}/lib/linux_x86_64/libCompressonator$<$<CONFIG:Debug>:d>.a"
"${COMPRESSONATOR_DIR}/lib/linux_x86_64/libCMP_Core$<$<CONFIG:Debug>:d>.a"
"${COMPRESSONATOR_DIR}/lib/linux_x86_64/libCMP_Core_AVX$<$<CONFIG:Debug>:d>.a"
"${COMPRESSONATOR_DIR}/lib/linux_x86_64/libCMP_Core_AVX512$<$<CONFIG:Debug>:d>.a"
"${COMPRESSONATOR_DIR}/lib/linux_x86_64/libCMP_Core_SSE$<$<CONFIG:Debug>:d>.a")
"${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCompressonator$<$<CONFIG:Debug>:d>.a"
"${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core$<$<CONFIG:Debug>:d>.a")
if(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
target_link_libraries(${TARGET} PRIVATE
"${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_AVX$<$<CONFIG:Debug>:d>.a"
"${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_AVX512$<$<CONFIG:Debug>:d>.a"
"${COMPRESSONATOR_DIR}/lib/linux_${CMAKE_SYSTEM_PROCESSOR}/libCMP_Core_SSE$<$<CONFIG:Debug>:d>.a")
endif()
else()
message(FATAL_ERROR "Unable to link to Compressonator library!")
endif()
Expand Down
102 changes: 102 additions & 0 deletions include/sourcepp/Bits.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#pragma once

#include <type_traits>

#include <sourcepp/Math.h>

namespace sourcepp::bits {

/// A view of a given numeric type `A` that is always little-endian in memory.
/// Typically becomes no-op at `-O2` on LE targets.
///
/// Any instance of `*reinterpret_cast<NUMERIC_TYPE *>` etc., or use of numeric types
/// in structures destined for I/O buffers is suspect, and, in either case, should likely
/// be replaced by some invocation of this class. This can be syntactically cumbersome,
/// so convenient aliases are provided with f32le, etc.
///
/// Special provisions for arithmetic outside possible implicit conversions are intentionally omitted.
template<sourcepp::math::Arithmetic A>
class LERep : private std::array<std::byte, sizeof(A)> {
using uint_according = typename std::conditional<sizeof(A) == 8,
uint64_t,
typename std::conditional<sizeof(A) == 4,
uint32_t,
typename std::conditional<sizeof(A) == 2,
uint16_t,
uint8_t>::type>::type>::type;
public:

/// Conversion to native byte order.
/// `LERep<A>` ⇒ `A`.
constexpr operator A() const {
uint_according ret = 0;
for (size_t offs = 0; auto &b : *this) {
ret |= (static_cast<uint_according>(b) << offs) & (uint_according(0xFFu) << offs);
offs += 8;
}
return *reinterpret_cast<A *>(&ret);
}

/// Conversion from native byte order.
/// `A` ⇒ `LERep<A>`.
constexpr LERep &operator=(const A &v) {
auto in = *reinterpret_cast<const uint_according *>(&v);
for (size_t offs = 0; auto &b : *this) {
b = static_cast<std::byte>((in >> offs) & uint_according(0xFFu));
offs += 8;
}
return *this;
}

/// Convenience for arithmetic conversion from some B.
/// (`B` ⇒ `A`) ⇒ (`B` ⇒ `LERep<A>`).
template<sourcepp::math::Arithmetic B> requires (std::convertible_to<B, A> || std::is_same_v<A, half>)
constexpr LERep(const B &u) { this->operator=(static_cast<A>(u)); }

/// Convenience for arithmetic conversion between LERep of distinct but arithmetically convertible types.
/// (`B` ⇒ `A`) ⇒ (`LERep<B>` ⇒ `LERep<A>`).
template<sourcepp::math::Arithmetic B> requires std::convertible_to<B, A>
constexpr LERep(const LERep<B> &u) { this->operator=(u.operator A()); }

/// Convenience for arithmetic conversion to some B.
/// (`LERep<A>` ⇒ `A`) ∧ (`A` ⇒ `B`) ⇒ (`LERep<A>` ⇒ `B`).
template<sourcepp::math::Arithmetic B> requires std::convertible_to<A, B>
constexpr operator B() const { return static_cast<B>(this->operator A()); }

/// Permits uninitialized LERep, for parity with the associated A.
constexpr LERep() {}

/// Construct a value from a byte buffer.
constexpr explicit LERep(std::span<const std::byte, sizeof(A)> arr) {
std::memcpy(this, arr.data(), sizeof(A));
}
};

/// Read a given value of type A from a pointer into arbitrary data presumed to hold a little-endian representation of A.
/// Subject to the usual litany of cautions about
///
/// *reinterpret_cast<T *>(p)
///
/// , such invocations, where spuriously dependent upon host byte order, may be replaced with:
///
/// deref_as_le<T>(p)
template<sourcepp::math::Arithmetic A>
[[nodiscard]] constexpr A deref_as_le(const void *p) {
return LERep<A>(std::span<const std::byte, sizeof(A)>(static_cast<const std::byte *>(p), sizeof(A))).operator A();
}

#define SOURCEPP_LEREP_DEFINE(N, V)\
using N##le = LERep<V>
#define SOURCEPP_LEREP_DEFINE_P(PFX, N16, N32, N64) \
SOURCEPP_LEREP_DEFINE(PFX##16, N16); \
SOURCEPP_LEREP_DEFINE(PFX##32, N32); \
SOURCEPP_LEREP_DEFINE(PFX##64, N64)

SOURCEPP_LEREP_DEFINE_P(i, int16_t, int32_t, int64_t);
SOURCEPP_LEREP_DEFINE_P(ui, uint16_t, uint32_t, uint64_t);
SOURCEPP_LEREP_DEFINE_P(f, half, float, double);

#undef SOURCEPP_LEREP_DEFINE_P
#undef SOURCEPP_LEREP_DEFINE

} // namespace sourcepp::bits
78 changes: 78 additions & 0 deletions include/sourcepp/Macros.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

// Helpers
#define SOURCEPP_CONCAT_DETAIL(a, b) a##b
/// Token pasting outside a macro context.
#define SOURCEPP_CONCAT(a, b) SOURCEPP_CONCAT_DETAIL(a, b)

/// Create a breakpoint in debug
Expand Down Expand Up @@ -70,3 +71,80 @@
static_cast<std::underlying_type_t<Enum>>(lhs) ^ \
static_cast<std::underlying_type_t<Enum>>(rhs)); \
}

/// Indirected `()`.
#define SOURCEPP_UNIT ()

#define SOURCEPP_EXPAND8(...) SOURCEPP_EXPAND7(SOURCEPP_EXPAND7(SOURCEPP_EXPAND7(SOURCEPP_EXPAND7(__VA_ARGS__))))
#define SOURCEPP_EXPAND7(...) SOURCEPP_EXPAND6(SOURCEPP_EXPAND6(SOURCEPP_EXPAND6(SOURCEPP_EXPAND6(__VA_ARGS__))))
#define SOURCEPP_EXPAND6(...) SOURCEPP_EXPAND5(SOURCEPP_EXPAND5(SOURCEPP_EXPAND5(SOURCEPP_EXPAND5(__VA_ARGS__))))
#define SOURCEPP_EXPAND5(...) SOURCEPP_EXPAND4(SOURCEPP_EXPAND4(SOURCEPP_EXPAND4(SOURCEPP_EXPAND4(__VA_ARGS__))))
#define SOURCEPP_EXPAND4(...) SOURCEPP_EXPAND3(SOURCEPP_EXPAND3(SOURCEPP_EXPAND3(SOURCEPP_EXPAND3(__VA_ARGS__))))
#define SOURCEPP_EXPAND3(...) SOURCEPP_EXPAND2(SOURCEPP_EXPAND2(SOURCEPP_EXPAND2(SOURCEPP_EXPAND2(__VA_ARGS__))))
#define SOURCEPP_EXPAND2(...) SOURCEPP_EXPAND1(SOURCEPP_EXPAND1(SOURCEPP_EXPAND1(SOURCEPP_EXPAND1(__VA_ARGS__))))
#define SOURCEPP_EXPAND1(...) __VA_ARGS__

#define SOURCEPP_ID(...) __VA_ARGS__

/// Apply a unary macro to each of `__VA_ARGS__`.
/// \param sep Nullary function-like macro expected to expand to a separator. For rationale, see \ref SOURCEPP_THUNK_COMMA.
/// \param macro Unary macro.
/// \param ... List of first arguments per call to `macro`.
#define SOURCEPP_FOREACH0_SEP(sep, macro, ...) \
__VA_OPT__(SOURCEPP_EXPAND5(SOURCEPP_FOREACH0_SEP_HELPER(sep, macro, __VA_ARGS__)))
#define SOURCEPP_FOREACH0_SEP_HELPER(sep, macro, x, ...) \
macro(x) \
__VA_OPT__(sep SOURCEPP_UNIT SOURCEPP_FOREACH0_SEP_HELPER_THUNK SOURCEPP_UNIT (sep, macro, __VA_ARGS__))
#define SOURCEPP_FOREACH0_SEP_HELPER_THUNK() SOURCEPP_FOREACH0_SEP_HELPER

/// Apply a binary macro to each of `__VA_ARGS__` with a set first argument.
/// \param sep Nullary function-like macro expected to expand to a separator. For rationale, see \ref SOURCEPP_THUNK_COMMA.
/// \param macro Binary macro.
/// \param a Always the first argument to `macro`.
/// \param ... List of second arguments per call to `macro`
#define SOURCEPP_FOREACH1_SEP(sep, macro, a, ...) \
__VA_OPT__(SOURCEPP_EXPAND5(SOURCEPP_FOREACH1_SEP_HELPER(sep, macro, a, __VA_ARGS__)))
#define SOURCEPP_FOREACH1_SEP_HELPER(sep, macro, a, x, ...) \
macro(a, x) \
__VA_OPT__(sep SOURCEPP_UNIT SOURCEPP_FOREACH1_SEP_HELPER_THUNK SOURCEPP_UNIT (sep, macro, a, __VA_ARGS__))
#define SOURCEPP_FOREACH1_SEP_HELPER_THUNK() SOURCEPP_FOREACH1_SEP_HELPER

/// Reverse an argument list; evaluates comma-separated but unparenthesized.
#define SOURCEPP_REVERSE(...) \
__VA_OPT__(SOURCEPP_EXPAND5(SOURCEPP_REVERSE_HELPER(__VA_ARGS__)))
#define SOURCEPP_REVERSE_HELPER(a, ...) \
__VA_OPT__(SOURCEPP_REVERSE_HELPER_THUNK SOURCEPP_UNIT (__VA_ARGS__),) a
#define SOURCEPP_REVERSE_HELPER_THUNK() SOURCEPP_REVERSE_HELPER

/// Nullary macro that expands to nothing.
#define SOURCEPP_THUNK_NOTHING()

/// Turn its operand into (effectively) a nullary function-like macro that expands to it.
#define SOURCEPP_THUNK(id) id SOURCEPP_THUNK_NOTHING
/// Nullary macro that expands to a comma. It is necessary to defer expansion to any commas in the
/// desired output of complex macro expansions, to prevent the preprocessor from interpreting such a comma itself.
#define SOURCEPP_THUNK_COMMA() ,

/// Convenience variant of SOURCEPP_FOREACH0_SEP with no separator.
#define SOURCEPP_FOREACH0(macro, ...) SOURCEPP_FOREACH0_SEP(SOURCEPP_THUNK_NOTHING, macro, __VA_ARGS__)
/// Convenience variant of SOURCEPP_FOREACH1_SEP with no separator.
#define SOURCEPP_FOREACH1(macro, a, ...) SOURCEPP_FOREACH1_SEP(SOURCEPP_THUNK_NOTHING, macro, a, __VA_ARGS__)

/// Callable parenthesization; identity function for 2-tuples when used bare as in:
///
/// SOURCEPP_CONS TUPLE
#define SOURCEPP_CONS(a, d) (a, d)
/// Called bare to destructure the first of a 2-tuple.
#define SOURCEPP_CAR(a, d) a
/// Called bare to destructure the second of a 2-tuple.
#define SOURCEPP_CDR(a, d) d

#define SOURCEPP_CALL_WITH_POLICY_IF_TBB(ident, policy, ...) ident(__VA_ARGS__)
#ifdef SOURCEPP_BUILD_WITH_TBB
# undef SOURCEPP_CALL_WITH_POLICY_IF_TBB
# if __has_include(<execution>)
# define SOURCEPP_CALL_WITH_POLICY_IF_TBB(ident, policy, ...) ident(policy __VA_OPT__(,) __VA_ARGS__)
# else
# define SOURCEPP_CALL_WITH_POLICY_IF_TBB(...) static_assert(false, "This macro needs <execution> present.")
# endif
#endif
Loading
Loading