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

feat: use std::threads for encoding / transcoding operations #6

Merged
merged 1 commit into from
Nov 14, 2024
Merged
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
199 changes: 122 additions & 77 deletions cpp/react-native-basis-universal.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "react-native-basis-universal.h"
#include "KTX2File.h"
#include "BasisFile.h"
#include <thread>
#include <future>

#define DEFINE_BASIS_ENCODER_PARAMS_SETTER(func_name, param_name, param_type) \
void ReactNativeBasisUniversal::func_name(jsi::Runtime &rt, jsi::Object handle, param_type flag) { \
Expand All @@ -26,6 +28,12 @@ using namespace basisu;

namespace facebook::react {

struct EncodeResult {
bool success;
std::vector<uint8_t> data;
};


class BasisEncoder : public jsi::NativeState {
public:
basis_compressor_params m_params;
Expand Down Expand Up @@ -89,80 +97,86 @@ int ReactNativeBasisUniversal::encode(jsi::Runtime &rt, jsi::Object handle, jsi:
throw jsi::JSError(rt, "Image Array needs to be ArrayBuffer");
}

auto arrayBuffer = basisFileData.getArrayBuffer(rt);

if (!basis_initialized_flag)
{
assert(0);
return 0;
}

job_pool jpool(6);

// Initialize the compression parameters structure. This is the same structure that the command line tool fills in.
basis_compressor_params &params = encoder->m_params;

params.m_pJob_pool = &jpool;

params.m_multithreading = true;

params.m_status_output = params.m_debug;

params.m_read_source_images = false;
params.m_write_output_basis_or_ktx2_files = false;

basis_compressor comp;

if (!comp.init(params))
{
return 0;
}

basis_compressor::error_code ec = comp.process();

if (ec != basis_compressor::cECSuccess)
{
// Something failed during compression.
return 0;
}

if (params.m_create_ktx2_file)
{
// Compression succeeded, so copy the .ktx2 file bytes to the caller's buffer.
auto output = comp.get_output_ktx2_file();

if (!output.data()) {
return 0;
std::promise<EncodeResult> resultPromise;
std::future<EncodeResult> resultFuture = resultPromise.get_future();

std::thread encodingThread([this,
encoder = std::move(encoder),
promise = std::move(resultPromise)]() mutable {

EncodeResult result{false, std::vector<uint8_t>()};

try {
if (!basis_initialized_flag) {
promise.set_value(std::move(result));
return;
}

unsigned int threads = std::thread::hardware_concurrency();
job_pool jpool(threads);
basis_compressor_params &params = encoder->m_params;
params.m_pJob_pool = &jpool;
params.m_multithreading = true;
params.m_status_output = params.m_debug;
params.m_read_source_images = false;
params.m_write_output_basis_or_ktx2_files = false;

basis_compressor comp;
if (!comp.init(params)) {
promise.set_value(std::move(result));
return;
}

basis_compressor::error_code ec = comp.process();
if (ec != basis_compressor::cECSuccess) {
promise.set_value(std::move(result));
return;
}

if (params.m_create_ktx2_file) {
auto output = comp.get_output_ktx2_file();
if (!output.data()) {
promise.set_value(std::move(result));
return;
}
result.data.assign(output.data(), output.data() + output.size());
} else {
auto output = comp.get_output_basis_file();
if (!output.data()) {
promise.set_value(std::move(result));
return;
}
result.data.assign(output.data(), output.data() + output.size());
}

result.success = true;
promise.set_value(std::move(result));
} catch (...) {
try {
promise.set_exception(std::current_exception());
} catch (...) {}
}

auto outputBuffer = jsi::ArrayBuffer(std::move(arrayBuffer));
memcpy(outputBuffer.data(rt), output.data(), output.size());
basisFileData.setProperty(rt, jsi::PropNameID::forAscii(rt, "buffer"), outputBuffer);


// Return the file size of the .basis file in bytes.
return (uint32_t)comp.get_output_ktx2_file().size();
}
else
{
auto output = comp.get_output_basis_file();

if (!output.data()) {
});

encodingThread.detach();

try {
auto result = resultFuture.get();
if (!result.success) {
return 0;
}

// Compression succeeded, so copy the .basis file bytes to the caller's buffer.
auto arrayBuffer = basisFileData.getArrayBuffer(rt);
auto outputBuffer = jsi::ArrayBuffer(std::move(arrayBuffer));
memcpy(outputBuffer.data(rt), output.data(), output.size());
memcpy(outputBuffer.data(rt), result.data.data(), result.data.size());
basisFileData.setProperty(rt, jsi::PropNameID::forAscii(rt, "buffer"), outputBuffer);

// Return the file size of the .basis file in bytes.
return (uint32_t)comp.get_output_basis_file().size();
return result.data.size();
} catch (const std::exception& e) {
throw jsi::JSError(rt, e.what());
}

return 0;
}


void ReactNativeBasisUniversal::setKTX2UASTCSupercompression(jsi::Runtime &rt, jsi::Object handle, bool flag) {
auto encoder = tryGetBasisEncoder(rt, handle);
encoder->m_params.m_ktx2_uastc_supercompression = flag ? basist::KTX2_SS_ZSTANDARD : basist::KTX2_SS_NONE;
Expand Down Expand Up @@ -216,6 +230,7 @@ bool ReactNativeBasisUniversal::setSliceSourceImage(jsi::Runtime &rt, jsi::Objec
return true;
}


bool ReactNativeBasisUniversal::setSliceSourceImageHDR(jsi::Runtime &rt, jsi::Object handle, int sliceIndex, jsi::Object imageArray, int width, int height, int imgType, bool ldrSrgbToLinear) {
if (!imageArray.isArrayBuffer(rt)) {
throw jsi::JSError(rt, "Image Array needs to be ArrayBuffer");
Expand Down Expand Up @@ -407,15 +422,47 @@ int ReactNativeBasisUniversal::getImageTranscodedSizeInBytes(jsi::Runtime &rt, j

int ReactNativeBasisUniversal::transcodeImage(jsi::Runtime &rt, jsi::Object handle, jsi::Object dst, int levelIndex, int layerIndex, int faceIndex, int format, int getAlphaForOpaqueFormats, int channel0, int channel1) {
auto ktx2Handle = tryGetKTX2Handle(rt, handle);
return ktx2Handle->transcodeImage(rt,
dst,
levelIndex,
layerIndex,
faceIndex,
format,
getAlphaForOpaqueFormats,
channel0,
channel1);

std::promise<int> resultPromise;
std::future<int> resultFuture = resultPromise.get_future();

std::thread transcodingThread([
this,
ktx2Handle = ktx2Handle,
promise = std::move(resultPromise),
&rt,
&dst,
levelIndex,
layerIndex,
faceIndex,
format,
getAlphaForOpaqueFormats,
channel0,
channel1
]() mutable {
try {
auto res = ktx2Handle->transcodeImage(rt,
dst,
levelIndex,
layerIndex,
faceIndex,
format,
getAlphaForOpaqueFormats,
channel0,
channel1);
promise.set_value(res);
} catch (...) {
promise.set_exception(std::current_exception());
}
});

transcodingThread.detach();

try {
return resultFuture.get();
} catch (const std::exception& e) {
throw jsi::JSError(rt, e.what());
}
}


Expand Down Expand Up @@ -507,6 +554,4 @@ jsi::Object ReactNativeBasisUniversal::getImageLevelDescBasisFile(jsi::Runtime &
return jsi::Object(rt);
}



}
4 changes: 2 additions & 2 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1237,7 +1237,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- react-native-basis-universal (0.1.0):
- react-native-basis-universal (0.2.1):
- DoubleConversion
- glog
- hermes-engine
Expand Down Expand Up @@ -1845,7 +1845,7 @@ SPEC CHECKSUMS:
React-logger: d79b704bf215af194f5213a6b7deec50ba8e6a9b
React-Mapbuffer: b982d5bba94a8bc073bda48f0d27c9b28417fae3
React-microtasksnativemodule: 2cec1d6e126598df0f165268afa231174dd1a611
react-native-basis-universal: 228f3b3e396df77064173f7a26622d45e424964e
react-native-basis-universal: 8c4230f3b21c9cb23109ba9d8aff32deb19cd1a6
react-native-blob-util: e6a3b23a99ac2c3d9fa48722db049a1e1808efc2
React-nativeconfig: 8c83d992b9cc7d75b5abe262069eaeea4349f794
React-NativeModulesApple: 9f7920224a3b0c7d04d77990067ded14cee3c614
Expand Down
Loading