mirror of
https://github.com/BinomialLLC/basis_universal.git
synced 2026-08-04 12:29:04 +00:00
adding new files
This commit is contained in:
1
python/lowlevel_test_wasm/__init__.py
Normal file
1
python/lowlevel_test_wasm/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# __init__.py
|
||||
58
python/lowlevel_test_wasm/basic_test.py
Normal file
58
python/lowlevel_test_wasm/basic_test.py
Normal file
@@ -0,0 +1,58 @@
|
||||
import wasmtime
|
||||
import ctypes
|
||||
|
||||
# --- Engine ---
|
||||
engine = wasmtime.Engine()
|
||||
|
||||
# --- Store ---
|
||||
store = wasmtime.Store(engine)
|
||||
|
||||
# --- WASI config ---
|
||||
wasi = wasmtime.WasiConfig()
|
||||
wasi.argv = ["basisu_module_st"]
|
||||
wasi.inherit_stdout() # <-- tell WASI to use the host stdout
|
||||
wasi.inherit_stderr()
|
||||
store.set_wasi(wasi)
|
||||
|
||||
# --- Load module ---
|
||||
module = wasmtime.Module.from_file(engine, "basisu_py/wasm/basisu_module_st.wasm")
|
||||
|
||||
# --- Linker + WASI ---
|
||||
linker = wasmtime.Linker(engine)
|
||||
linker.define_wasi()
|
||||
|
||||
# --- Instantiate ---
|
||||
instance = linker.instantiate(store, module)
|
||||
print("Single-threaded WASM instantiated OK")
|
||||
|
||||
# --- Exports ---
|
||||
exports = instance.exports(store)
|
||||
|
||||
get_version = exports["bu_get_version"]
|
||||
alloc = exports["bu_alloc"]
|
||||
free = exports["bu_free"]
|
||||
memory = exports["memory"]
|
||||
|
||||
# --- Version ---
|
||||
version = get_version(store)
|
||||
print("Version =", version)
|
||||
|
||||
# --- Alloc ---
|
||||
ptr = alloc(store, 64)
|
||||
print("Allocated ptr =", ptr)
|
||||
|
||||
# --- Access WASM memory properly ---
|
||||
data_len = memory.data_len(store)
|
||||
raw_ptr = memory.data_ptr(store) # ctypes pointer
|
||||
addr = ctypes.addressof(raw_ptr.contents) # convert to integer pointer
|
||||
|
||||
# Create a byte array view into WASM memory
|
||||
buf = (ctypes.c_ubyte * data_len).from_address(addr)
|
||||
|
||||
# Write TEST at allocated ptr
|
||||
buf[ptr : ptr + 4] = b"TEST"
|
||||
print("Wrote TEST into WASM memory.")
|
||||
|
||||
# --- Free ---
|
||||
free(store, ptr)
|
||||
print("Memory free OK.")
|
||||
148
python/lowlevel_test_wasm/basisu_wasm.py
Normal file
148
python/lowlevel_test_wasm/basisu_wasm.py
Normal file
@@ -0,0 +1,148 @@
|
||||
# basisu_wasm.py
|
||||
import wasmtime
|
||||
import ctypes
|
||||
import sys
|
||||
|
||||
sys.path.append("basisu_py") # our shared .py files
|
||||
|
||||
from constants import *
|
||||
|
||||
class BasisuWasm:
|
||||
def __init__(self, path):
|
||||
self.path = path
|
||||
self.engine = None
|
||||
self.store = None
|
||||
self.memory = None
|
||||
self.exports = None
|
||||
|
||||
# -----------------------------------------------
|
||||
# Internal helper: build WASI + Wasmtime engine
|
||||
# -----------------------------------------------
|
||||
def _init_engine(self):
|
||||
self.engine = wasmtime.Engine()
|
||||
self.store = wasmtime.Store(self.engine)
|
||||
|
||||
wasi = wasmtime.WasiConfig()
|
||||
wasi.argv = ["basisu"]
|
||||
wasi.inherit_stdout()
|
||||
wasi.inherit_stderr()
|
||||
self.store.set_wasi(wasi)
|
||||
|
||||
return wasi
|
||||
|
||||
# -----------------------------------------------
|
||||
# Create linker and instantiate WASM module
|
||||
# -----------------------------------------------
|
||||
def load(self):
|
||||
self._init_engine()
|
||||
|
||||
module = wasmtime.Module.from_file(self.engine, self.path)
|
||||
linker = wasmtime.Linker(self.engine)
|
||||
linker.define_wasi()
|
||||
|
||||
instance = linker.instantiate(self.store, module)
|
||||
|
||||
self.exports = instance.exports(self.store)
|
||||
self.memory = self.exports["memory"]
|
||||
|
||||
if "bu_init" in self.exports:
|
||||
self.exports["bu_init"](self.store)
|
||||
|
||||
print("WASM loaded:", self.path)
|
||||
|
||||
# -----------------------------------------------
|
||||
# Read/write WASM linear memory via ctypes
|
||||
# -----------------------------------------------
|
||||
def _wasm_buf(self):
|
||||
raw_ptr = self.memory.data_ptr(self.store)
|
||||
length = self.memory.data_len(self.store)
|
||||
addr = ctypes.addressof(raw_ptr.contents)
|
||||
return (ctypes.c_ubyte * length).from_address(addr)
|
||||
|
||||
# -----------------------------------------------
|
||||
# Exported API accessors
|
||||
# -----------------------------------------------
|
||||
def init(self):
|
||||
return self.exports["bu_init"](self.store)
|
||||
|
||||
def version(self):
|
||||
return self.exports["bu_get_version"](self.store)
|
||||
|
||||
def alloc(self, size):
|
||||
return self.exports["bu_alloc"](self.store, size)
|
||||
|
||||
def free(self, ptr):
|
||||
return self.exports["bu_free"](self.store, ptr)
|
||||
|
||||
def new_params(self):
|
||||
return self.exports["bu_new_comp_params"](self.store)
|
||||
|
||||
def delete_params(self, ptr):
|
||||
return self.exports["bu_delete_comp_params"](self.store, ptr)
|
||||
|
||||
def set_image_rgba32(self, params, image_index, img_ptr, w, h, pitch):
|
||||
return self.exports["bu_comp_params_set_image_rgba32"](
|
||||
self.store, params, image_index, img_ptr, w, h, pitch
|
||||
)
|
||||
|
||||
def set_image_float_rgba(self, params, image_index, img_ptr, w, h, pitch):
|
||||
return self.exports["bu_comp_params_set_image_float_rgba"](
|
||||
self.store, params, image_index, img_ptr, w, h, pitch
|
||||
)
|
||||
|
||||
# Normally quality_level controls the quality.
|
||||
# If quality_level==-1, then rdo_quality (a low-level parameter) directly
|
||||
# controls each codec's quality setting. Normally set to 0.
|
||||
|
||||
def compress_texture_lowlevel(self, params,
|
||||
tex_format,
|
||||
quality_level,
|
||||
effort_level,
|
||||
flags_and_quality,
|
||||
rdo_quality):
|
||||
|
||||
return self.exports["bu_compress_texture"](
|
||||
self.store,
|
||||
params,
|
||||
tex_format,
|
||||
quality_level,
|
||||
effort_level,
|
||||
flags_and_quality,
|
||||
rdo_quality
|
||||
)
|
||||
|
||||
def compress(self, params,
|
||||
tex_format=BasisTexFormat.cUASTC_LDR_4x4,
|
||||
quality=BasisQuality.MAX,
|
||||
effort=BasisEffort.DEFAULT,
|
||||
flags=BasisFlags.NONE,
|
||||
rdo_quality=0.0):
|
||||
|
||||
return bool(self.compress_texture_lowlevel(
|
||||
params,
|
||||
tex_format,
|
||||
quality,
|
||||
effort,
|
||||
flags,
|
||||
rdo_quality
|
||||
))
|
||||
|
||||
def get_comp_data_ofs(self, params):
|
||||
return self.exports["bu_comp_params_get_comp_data_ofs"](self.store, params)
|
||||
|
||||
def get_comp_data_size(self, params):
|
||||
return self.exports["bu_comp_params_get_comp_data_size"](self.store, params)
|
||||
|
||||
# -----------------------------------------------
|
||||
# Copy bytes into WASM memory
|
||||
# -----------------------------------------------
|
||||
def write_bytes(self, wasm_ptr, data: bytes):
|
||||
buf = self._wasm_buf()
|
||||
buf[wasm_ptr:wasm_ptr+len(data)] = data
|
||||
|
||||
# -----------------------------------------------
|
||||
# Read bytes from WASM memory
|
||||
# -----------------------------------------------
|
||||
def read_bytes(self, wasm_ptr, size):
|
||||
buf = self._wasm_buf()
|
||||
return bytes(buf[wasm_ptr:wasm_ptr+size])
|
||||
63
python/lowlevel_test_wasm/compress_test.py
Normal file
63
python/lowlevel_test_wasm/compress_test.py
Normal file
@@ -0,0 +1,63 @@
|
||||
# compress_test.py
|
||||
from .basisu_wasm import *
|
||||
|
||||
# === Load WASM ===
|
||||
codec = BasisuWasm("basisu_py/wasm/basisu_module_st.wasm")
|
||||
codec.load()
|
||||
|
||||
print("Version =", codec.version())
|
||||
|
||||
# === Build test image ===
|
||||
W, H = 256, 256
|
||||
BYTES_PER_PIXEL = 4
|
||||
pitch = W * BYTES_PER_PIXEL
|
||||
|
||||
img = bytearray(W * H * 4)
|
||||
|
||||
for y in range(H):
|
||||
for x in range(W):
|
||||
i = (y * W + x) * 4
|
||||
img[i + 0] = x & 0xFF # R
|
||||
img[i + 1] = y & 0xFF # G
|
||||
img[i + 2] = (x ^ y) & 0xFF # B
|
||||
img[i + 3] = 255 # A
|
||||
|
||||
# === Upload image to WASM memory ===
|
||||
img_ptr = codec.alloc(len(img))
|
||||
codec.write_bytes(img_ptr, img)
|
||||
|
||||
# === Create comp_params ===
|
||||
params = codec.new_params()
|
||||
|
||||
# === Set image into comp_params ===
|
||||
ok = codec.set_image_rgba32(params, 0, img_ptr, W, H, pitch)
|
||||
print("Set image:", ok)
|
||||
|
||||
# === Compress ===
|
||||
ok = codec.compress(
|
||||
params,
|
||||
tex_format=BasisTexFormat.cUASTC_LDR_4x4,
|
||||
quality=100,
|
||||
effort=BasisEffort.DEFAULT,
|
||||
flags=BasisFlags.KTX2_OUTPUT | BasisFlags.SRGB,
|
||||
rdo_quality=0.0
|
||||
)
|
||||
print("Compress result:", ok)
|
||||
|
||||
# === Retrieve compressed blob ===
|
||||
ofs = codec.get_comp_data_ofs(params)
|
||||
size = codec.get_comp_data_size(params)
|
||||
print("Output size =", size)
|
||||
|
||||
comp_data = codec.read_bytes(ofs, size)
|
||||
print("First 16 bytes:", comp_data[:16])
|
||||
|
||||
# === Save to KTX2 ===
|
||||
with open("test.ktx2", "wb") as f:
|
||||
f.write(comp_data)
|
||||
|
||||
print("File written: test.ktx2")
|
||||
|
||||
# === Cleanup ===
|
||||
codec.delete_params(params)
|
||||
codec.free(img_ptr)
|
||||
76
python/lowlevel_test_wasm/compress_test_float.py
Normal file
76
python/lowlevel_test_wasm/compress_test_float.py
Normal file
@@ -0,0 +1,76 @@
|
||||
# compress_test_float.py
|
||||
|
||||
from .basisu_wasm import BasisuWasm, BasisTexFormat, BasisEffort, BasisFlags, BasisQuality
|
||||
import struct # for packing floats
|
||||
|
||||
# === Load WASM ===
|
||||
codec = BasisuWasm("basisu_py/wasm/basisu_module_st.wasm")
|
||||
codec.load()
|
||||
|
||||
print("Version =", codec.version())
|
||||
|
||||
# === Build a 256x256 FLOAT RGBA image ===
|
||||
W, H = 256, 256
|
||||
BYTES_PER_PIXEL = 16 # float32 * 4
|
||||
pitch = W * BYTES_PER_PIXEL
|
||||
|
||||
# Float image stored as bytearray of packed floats
|
||||
img = bytearray(W * H * BYTES_PER_PIXEL)
|
||||
|
||||
for y in range(H):
|
||||
for x in range(W):
|
||||
# Create some float HDR gradient pattern
|
||||
r = float(x) / W # 0.0 ? 1.0
|
||||
g = float(y) / H # 0.0 ? 1.0
|
||||
b = float(x ^ y) / 255.0 # quirky pattern
|
||||
a = 1.0
|
||||
|
||||
i = (y * W + x) * 4
|
||||
|
||||
# pack into img bytearray
|
||||
struct.pack_into("ffff", img, i*4, r, g, b, a)
|
||||
|
||||
print("Created FLOAT RGBA image.")
|
||||
|
||||
# === Upload to WASM memory ===
|
||||
img_ptr = codec.alloc(len(img))
|
||||
codec.write_bytes(img_ptr, img)
|
||||
print("Copied float image into WASM heap at", img_ptr)
|
||||
|
||||
# === Create params ===
|
||||
params = codec.new_params()
|
||||
|
||||
# === Set FLOAT RGBA image ===
|
||||
ok = codec.set_image_float_rgba(params, 0, img_ptr, W, H, pitch)
|
||||
print("Set float RGBA:", ok)
|
||||
|
||||
# === Compress using HDR UASTC 4x4 ===
|
||||
ok = codec.compress(
|
||||
params,
|
||||
tex_format=BasisTexFormat.cUASTC_HDR_4x4,
|
||||
quality=BasisQuality.MAX,
|
||||
effort=BasisEffort.DEFAULT,
|
||||
flags=BasisFlags.KTX2_OUTPUT | BasisFlags.REC2020, # optional: HDR color space
|
||||
rdo_quality=0.0
|
||||
)
|
||||
|
||||
print("Compression result:", ok)
|
||||
|
||||
# === Retrieve compressed HDR KTX2 ===
|
||||
ofs = codec.get_comp_data_ofs(params)
|
||||
size = codec.get_comp_data_size(params)
|
||||
|
||||
print("Output size =", size)
|
||||
data = codec.read_bytes(ofs, size)
|
||||
|
||||
print("First 16 bytes:", data[:16])
|
||||
|
||||
# === Save to test_hdr.ktx2 ===
|
||||
with open("test_hdr.ktx2", "wb") as f:
|
||||
f.write(data)
|
||||
|
||||
print("Wrote test_hdr.ktx2")
|
||||
|
||||
# === Cleanup ===
|
||||
codec.delete_params(params)
|
||||
codec.free(img_ptr)
|
||||
Reference in New Issue
Block a user