Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ae333a3
chore(core-debug): add spdlog debug logging and ban raw std::cout
Prachi-Gupta2808 Jul 16, 2026
db35d9e
Update core/include/internal/gpu.h
Prachi-Gupta2808 Jul 16, 2026
45e1a0f
Update .github/workflows/no-raw-debug-output.yml
Prachi-Gupta2808 Jul 16, 2026
018bdcc
chore(core): add spdlog debug logging and ban raw std::cout
Prachi-Gupta2808 Jul 23, 2026
2702dff
Merge branch 'chore/spdlog-debug-logging' of https://github.com/Prach…
Prachi-Gupta2808 Jul 23, 2026
63e2eab
fix spdlog
Krasner Jul 24, 2026
520e250
restore dawn
Krasner Jul 24, 2026
1c18309
Merge branch 'dev' into chore/spdlog-debug-logging
Krasner Jul 24, 2026
40473dd
format
Krasner Jul 24, 2026
82063ba
Update .github/workflows/no-raw-debug-output.yml
Krasner Jul 26, 2026
2f37c8e
Merge branch 'dev' into chore/spdlog-debug-logging
Krasner Jul 26, 2026
c4ea135
Merge branch 'dev' into chore/spdlog-debug-logging
Ryan-Millard Aug 3, 2026
5e1ae08
Merge branch 'dev' into chore/spdlog-debug-logging
Ryan-Millard Aug 20, 2026
45884e6
feat(core): add IMG2NUM_LOG_LEVEL for configurable compile-time logging
Ryan-Millard Aug 20, 2026
037cc70
ci: fix inverted exit-code handling in raw debug output check
Ryan-Millard Aug 20, 2026
20207c3
fix(py): include spdlog in sdist and collect licenses into the wheel
Ryan-Millard Aug 20, 2026
df2bcd6
temp
Ryan-Millard Aug 24, 2026
1844ba6
chore: move C++ lint/format tooling to uv-managed scripts/py workspace
Ryan-Millard Aug 25, 2026
906a509
chore: unify lint/format scripts under lint:* and format:* namespaces
Ryan-Millard Aug 25, 2026
9a2b37a
build: persist scikit-build CMake tree for clang-tidy coverage
Ryan-Millard Aug 25, 2026
b670ea6
build: lint wasm TUs against build-wasm compile database
Ryan-Millard Aug 25, 2026
1eea3e4
revert: out of scope packages/js change for licensing
Ryan-Millard Aug 25, 2026
f8a361d
fix(ci.yml): uv sync errors during lint
Ryan-Millard Aug 25, 2026
ff21cc8
fix(editorconfig): ignore generated files
Ryan-Millard Aug 25, 2026
9915ab0
fix: remove accidentally leftover file licenses.plugin.mjs
Ryan-Millard Aug 25, 2026
7959e05
style: fix editorconfig errors
Ryan-Millard Aug 25, 2026
6e3dc31
ci: skip wasm CMake prebuild in docs job via env flag
Ryan-Millard Aug 25, 2026
feafe09
revert: "ci: skip wasm CMake prebuild in docs job via env flag"
Ryan-Millard Aug 26, 2026
819935c
ci: fix docs build by pull spdlog
Ryan-Millard Aug 26, 2026
fdd06b1
ci: fix docs build errors
Ryan-Millard Aug 26, 2026
c85c987
UNDO!!! -> trigger release.yml on this PR temporarily for testing
Ryan-Millard Aug 26, 2026
0f70eb1
build(packages/js): drop just prebuild hook, add config-time wasm guard
Ryan-Millard Aug 26, 2026
1adf366
Merge branch 'dev' into chore/spdlog-debug-logging
Ryan-Millard Aug 26, 2026
979b939
style(vite.config.js): format the file
Ryan-Millard Aug 26, 2026
628d442
revert: "UNDO!!! -> trigger release.yml on this PR temporarily for te…
Ryan-Millard Aug 26, 2026
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
40 changes: 40 additions & 0 deletions .github/workflows/no-raw-debug-output.yml
Comment thread
Krasner marked this conversation as resolved.
Resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: CI / No Raw Debug Output

on:
pull_request:
push:
branches: [dev, main]

permissions:
contents: read

jobs:
check-debug-output:
name: Check for raw debug output
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install ripgrep
run: sudo apt-get install -y ripgrep

- name: Check for std::cout
run: |
if rg "std::cout" \
--type cpp \
-g '!third_party/**' \
-g '!example-apps/**' \
-l; then
echo "ERROR: Raw std::cout found. Use IMG2NUM_LOG_* macros instead."
exit 1
fi

- name: Check for std::cerr
run: |
if rg "std::cerr" \
--type cpp \
-g '!third_party/**' \
-g '!example-apps/**' \
-l; then
echo "ERROR: Raw std::cerr found. Use IMG2NUM_LOG_* macros instead."
exit 1
fi
Comment thread
Krasner marked this conversation as resolved.
Resolved
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
[submodule "third_party/stb"]
path = third_party/stb
url = https://github.com/nothings/stb.git
[submodule "third_party/spdlog"]
path = third_party/spdlog
url = https://github.com/gabime/spdlog.git
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ option(IMG2NUM_BUILD_C "Build C bindings (required for WASM builds)" ON)
option(IMG2NUM_BUILD_PYTHON "Build Python bindings" OFF)
option(IMG2NUM_BUILD_EXAMPLES "Build example applications" ON)
option(IMG2NUM_DEBUG_CACHE_VARIABLES_DUMP "Dump CMake environment variables in Debug mode" ON)
option(IMG2NUM_ENABLE_DEBUG_LOGGING "Compile with debug logs allowed to show in the console" ON)

# Force a safe default to avoid the weirdness of CMake's `None`
if(NOT CMAKE_BUILD_TYPE OR CMAKE_BUILD_TYPE STREQUAL "")
Expand Down
11 changes: 11 additions & 0 deletions core/CMakeLists.txt
Comment thread
Ryan-Millard marked this conversation as resolved.
Resolved
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ add_custom_target(Img2Num_shaders ALL

add_library(Img2Num ${CORE_SRC})

# spdlog (debug logging)
if(IMG2NUM_ENABLE_DEBUG_LOGGING AND NOT EMSCRIPTEN)
Comment thread
Prachi-Gupta2808 marked this conversation as resolved.
Resolved
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../third_party/spdlog"
"${CMAKE_CURRENT_SOURCE_DIR}/../third_party/spdlog/build/"
EXCLUDE_FROM_ALL)
target_link_libraries(Img2Num PRIVATE spdlog::spdlog_header_only)
target_compile_definitions(Img2Num PRIVATE IMG2NUM_ENABLE_DEBUG_LOGGING)
set_target_properties(spdlog_header_only PROPERTIES EXPORT_NAME spdlog_header_only)
install(TARGETS spdlog_header_only EXPORT Img2NumTargets)
Comment thread
Prachi-Gupta2808 marked this conversation as resolved.
Resolved
endif()

target_compile_options(Img2Num PRIVATE ${IMG2NUM_STRICT_CXX_FLAGS})

set_target_properties(Img2Num PROPERTIES
Expand Down
22 changes: 22 additions & 0 deletions core/include/internal/debug.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#pragma once

#ifdef IMG2NUM_ENABLE_DEBUG_LOGGING
#include <spdlog/spdlog.h>
#define IMG2NUM_LOG_INFO(...) spdlog::info(__VA_ARGS__)
#define IMG2NUM_LOG_WARN(...) spdlog::warn(__VA_ARGS__)
#define IMG2NUM_LOG_ERROR(...) spdlog::error(__VA_ARGS__)
#define IMG2NUM_LOG_DEBUG(...) spdlog::debug(__VA_ARGS__)
#else
#define IMG2NUM_LOG_INFO(...) \
do { \
} while (0)
#define IMG2NUM_LOG_WARN(...) \
do { \
} while (0)
#define IMG2NUM_LOG_ERROR(...) \
do { \
} while (0)
#define IMG2NUM_LOG_DEBUG(...) \
do { \
} while (0)
Comment thread
Prachi-Gupta2808 marked this conversation as resolved.
Resolved
#endif
66 changes: 35 additions & 31 deletions core/include/internal/gpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
#include <emscripten/html5.h>
#endif

#include "internal/debug.h"

#include <fstream>
#include <iostream>
#include <iterator>
#include <map>
#include <sstream>
Expand Down Expand Up @@ -117,12 +118,12 @@ class GPU {

for (uint32_t i = 0; i < info->messageCount; ++i) {
const auto& msg = info->messages[i];
std::cerr << "Shader Error ["
<< (msg.type == wgpu::CompilationMessageType::Error ? "ERR" : "WARN")
<< "]"
<< " Line " << msg.lineNum << ":" << msg.linePos << " - "
<< msg.message.data // .data for StringView
<< std::endl;
IMG2NUM_LOG_ERROR(
"Shader Error [{}] Line {}:{} - {}",
msg.type == wgpu::CompilationMessageType::Error ? "ERR" : "WARN",
msg.lineNum, msg.linePos,
std::string_view(msg.message.data ? msg.message.data : "", msg.message.length)
);
}
}
);
Expand All @@ -136,14 +137,14 @@ class GPU {
instance = wgpu::CreateInstance(&instanceDesc);

if (!instance) {
std::cerr << "Fatal: WebGPU instance creation failed." << std::endl;
IMG2NUM_LOG_ERROR("Fatal: WebGPU instance creation failed.");
return;
}

// ---------------------------------------------------------
// 1. Get Adapter
// ---------------------------------------------------------
std::cout << "Requesting Adapter..." << std::endl;
IMG2NUM_LOG_INFO("Requesting Adapter...");
adapter_ready = false;

instance.RequestAdapter(
Expand All @@ -152,11 +153,11 @@ class GPU {
[this](wgpu::RequestAdapterStatus status, wgpu::Adapter a, wgpu::StringView msg) {
if (status == wgpu::RequestAdapterStatus::Success) {
adapter = std::move(a);
std::cout << "Adapter Acquired" << std::endl;
IMG2NUM_LOG_INFO("Adapter Acquired");
} else {
std::cerr << "Adapter Failed: "
<< std::string_view(msg.data ? msg.data : "", msg.length)
<< std::endl;
IMG2NUM_LOG_ERROR(
"Adapter Failed: {}", std::string_view(msg.data ? msg.data : "", msg.length)
);
}
adapter_ready = true; // Unblock the loop
}
Expand All @@ -171,14 +172,14 @@ class GPU {
}

if (!adapter) {
std::cerr << "Fatal: Could not get WebGPU Adapter." << std::endl;
IMG2NUM_LOG_ERROR("Fatal: Could not get WebGPU Adapter.");
return;
}

// ---------------------------------------------------------
// 2. Get Device
// ---------------------------------------------------------
std::cout << "Requesting Device..." << std::endl;
IMG2NUM_LOG_INFO("Requesting Device...");
device_ready = false;

wgpu::DeviceDescriptor deviceDesc = {};
Expand All @@ -189,18 +190,19 @@ class GPU {
: "Unknown Error (Null message)";

// 2. Print it safely
std::cerr << "\n[WEBGPU FATAL ERROR] Type: " << static_cast<uint32_t>(type)
<< " | Msg: " << err_str << "\n"
<< std::endl;
IMG2NUM_LOG_ERROR(
"\n[WEBGPU FATAL ERROR] Type: {} | Msg: {}\n", static_cast<uint32_t>(type), err_str
);
});
deviceDesc.SetDeviceLostCallback(
wgpu::CallbackMode::AllowProcessEvents,
[](const wgpu::Device&, wgpu::DeviceLostReason reason, wgpu::StringView msg) {
std::string err_msg = (msg.data && msg.length > 0)
? std::string(msg.data, msg.length)
: "Unknown device lost reason";
std::cerr << "[DEVICE LOST] Reason: " << static_cast<int>(reason)
<< " Msg: " << err_msg << std::endl;
IMG2NUM_LOG_ERROR(
"[DEVICE LOST] Reason: {} Msg: {}", static_cast<int>(reason), err_msg
);
}
);

Expand All @@ -213,9 +215,10 @@ class GPU {
// Copy the adapter's physical limits over to your requested limits
requiredLimits = supportedLimits;

std::cout << "maxBufferSize: " << requiredLimits.maxBufferSize << std::endl;
std::cout << "maxStorageBufferBindingSize: "
<< requiredLimits.maxStorageBufferBindingSize << std::endl;
IMG2NUM_LOG_INFO("maxBufferSize: {}", requiredLimits.maxBufferSize);
IMG2NUM_LOG_INFO(
"maxStorageBufferBindingSize: {}", requiredLimits.maxStorageBufferBindingSize
);

deviceDesc.requiredLimits = &requiredLimits;
}
Expand All @@ -226,12 +229,13 @@ class GPU {
[this](wgpu::RequestDeviceStatus status, wgpu::Device d, wgpu::StringView msg) {
if (status == wgpu::RequestDeviceStatus::Success) {
device = std::move(d);
std::cout << "Device Acquired" << std::endl;
IMG2NUM_LOG_INFO("Device Acquired");
} else {
std::cerr << "Device Failed: "
<< (msg.data && msg.length > 0 ? std::string(msg.data, msg.length)
: "Unknown error")
<< std::endl;
IMG2NUM_LOG_ERROR(
"Device Failed: {}", msg.data && msg.length > 0
? std::string(msg.data, msg.length)
: "Unknown error"
);
}
device_ready = true; // Unblock the loop
}
Expand All @@ -246,18 +250,18 @@ class GPU {
}

if (!device) {
std::cerr << "Fatal: Could not get WebGPU Device." << std::endl;
IMG2NUM_LOG_ERROR("Fatal: Could not get WebGPU Device.");
return;
}

if (!validate_device()) {
std::cerr << "Fatal: Could not get WebGPU Device." << std::endl;
IMG2NUM_LOG_ERROR("Fatal: Could not get WebGPU Device.");
return;
}

queue = device.GetQueue();
gpu_initialized = true;
std::cout << "GPU Fully Initialized." << std::endl;
IMG2NUM_LOG_INFO("GPU Fully Initialized.");
};

~GPU() {
Expand Down
22 changes: 10 additions & 12 deletions core/src/internal/bilateral_filter_gpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include "img2num.h"
#include "internal/cielab.h"
#include "internal/debug.h"
#include "internal/gpu.h"

#include <algorithm>
Expand All @@ -10,7 +11,6 @@
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <iostream>
#include <vector>

static constexpr uint8_t COLOR_SPACE_OPTION_CIELAB {0};
Expand Down Expand Up @@ -52,7 +52,7 @@ void bilateral_filter_gpu(
std::memcpy(result.data(), image, width * height * 4);
// CIELAB conversion will run as shader

std::cout << "begin wgpu portion" << std::endl;
IMG2NUM_LOG_DEBUG("begin wgpu portion");
// 1. Create Input Texture
wgpu::TextureDescriptor texDesc = {};
texDesc.size = {static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1};
Expand All @@ -61,7 +61,7 @@ void bilateral_filter_gpu(
texDesc.usage = wgpu::TextureUsage::TextureBinding | wgpu::TextureUsage::CopyDst;
wgpu::Texture inputTexture = GPU::getClassInstance().get_device().CreateTexture(&texDesc);

std::cout << "upload texture" << std::endl;
IMG2NUM_LOG_DEBUG("upload texture");
// Upload data to Input Texture
wgpu::TexelCopyTextureInfo dst = {};
dst.texture = inputTexture;
Expand All @@ -74,7 +74,7 @@ void bilateral_filter_gpu(
&dst, image, bytesPerPixel * width * height, &layout, &texDesc.size
);

std::cout << "create output texture" << std::endl;
IMG2NUM_LOG_DEBUG("create output texture");
// 2. Create Output Texture (Storage)
wgpu::TextureDescriptor outDesc = texDesc;
outDesc.usage = wgpu::TextureUsage::StorageBinding | wgpu::TextureUsage::CopySrc;
Expand All @@ -89,7 +89,7 @@ void bilateral_filter_gpu(
// filtered lab
wgpu::Texture texLabFiltered = GPU::getClassInstance().get_device().CreateTexture(&descLab);

std::cout << "create buffer" << std::endl;
IMG2NUM_LOG_DEBUG("create buffer");
// 3. Create Uniform Buffer
float sr = static_cast<float>(sigma_range);
FilterParams params = {static_cast<float>(sigma_spatial), sr, 0.0f, 0.0f};
Expand Down Expand Up @@ -230,7 +230,7 @@ void bilateral_filter_gpu(

wgpu::CommandBuffer commands = encoder.Finish();
GPU::getClassInstance().get_queue().Submit(1, &commands);
std::cout << "queue submit" << std::endl;
IMG2NUM_LOG_DEBUG("queue submit");

// static volatile bool waiting = true;

Expand All @@ -250,31 +250,29 @@ void bilateral_filter_gpu(
readBuffer.MapAsync(
wgpu::MapMode::Read, 0, bufferSize, wgpu::CallbackMode::AllowProcessEvents,
[](wgpu::MapAsyncStatus status, wgpu::StringView message, void* userdata) {
std::cout << "In callback" << std::endl;
IMG2NUM_LOG_DEBUG("In callback");
bool* flag = static_cast<bool*>(userdata);
bool success = false;
if (status == wgpu::MapAsyncStatus::Success) {
success = true;
// std::cout << "Map success: " << message.data << std::endl;
} else {
// Handle error
success = false;
// std::cerr << "Map failed: " << message.data << std::endl;
}
Comment thread
Prachi-Gupta2808 marked this conversation as resolved.
Resolved
*flag = false;
},
(void*)waiting
);

std::cout << "waiting " << *waiting << std::endl;
IMG2NUM_LOG_DEBUG("waiting {}", *waiting);

while (*waiting) {
GPU::getClassInstance().get_instance().ProcessEvents();
#if defined(__EMSCRIPTEN__)
emscripten_sleep(10);
#endif
}
std::cout << "done wgpu" << std::endl;
IMG2NUM_LOG_DEBUG("done wgpu");
const uint8_t* mappedData = (const uint8_t*)readBuffer.GetConstMappedRange(0, bufferSize);
// copy to cpu buffer
for (size_t y = 0; y < height; ++y) {
Expand All @@ -291,7 +289,7 @@ void bilateral_filter_gpu(
}
readBuffer.Unmap();
std::memcpy(image, result.data(), result.size());
std::cout << "done memcpy" << std::endl;
IMG2NUM_LOG_DEBUG("done memcpy");

// explicit clean up

Expand Down
Loading