UIModule Enhancements: - Add ui:set_text topic handler to update widget text dynamically (UILabel support) - Add example: slider value updates linked label via game module coordination - Add timestamp logging for IIO latency measurement (T0-T3 tracking) Documentation Restructure: - Split UIModule README.md (600+ lines) into focused docs: * docs/UI_WIDGETS.md - Widget properties and JSON configuration * docs/UI_TOPICS.md - IIO topics reference and usage patterns * docs/UI_ARCHITECTURE.md - Threading model, limitations, design principles - Update CLAUDE.md with clear references to UIModule docs - Add warning: "READ BEFORE WORKING ON UI" for essential docs Asset Path Fixes: - Change test_ui_showcase texture paths from ../../assets to assets/ - Tests now run from project root (./build/tests/test_ui_showcase) - Add texture loading success/failure logs to TextureLoader and ResourceCache IIO Performance: - Re-enable batch flush thread in IntraIOManager (was disabled for debugging) - Document message latency: ~16ms in single-threaded tests, <1ms with threading - Clarify intentional architecture: no direct data binding, all via IIO topics Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
100 lines
3.4 KiB
C++
100 lines
3.4 KiB
C++
#include "TextureLoader.h"
|
|
#include "../RHI/RHIDevice.h"
|
|
|
|
#define STB_IMAGE_IMPLEMENTATION
|
|
#include <stb/stb_image.h>
|
|
|
|
#include <fstream>
|
|
#include <spdlog/spdlog.h>
|
|
|
|
namespace grove {
|
|
|
|
TextureLoader::LoadResult TextureLoader::loadFromFile(rhi::IRHIDevice& device, const std::string& path) {
|
|
LoadResult result;
|
|
|
|
spdlog::info("📂 TextureLoader: Loading texture from '{}'", path);
|
|
|
|
// Read file into memory
|
|
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
|
if (!file.is_open()) {
|
|
result.error = "Failed to open file: " + path;
|
|
spdlog::error("❌ TextureLoader: FAILED to open file '{}'", path);
|
|
return result;
|
|
}
|
|
|
|
std::streamsize size = file.tellg();
|
|
file.seekg(0, std::ios::beg);
|
|
|
|
std::vector<uint8_t> buffer(static_cast<size_t>(size));
|
|
if (!file.read(reinterpret_cast<char*>(buffer.data()), size)) {
|
|
result.error = "Failed to read file: " + path;
|
|
spdlog::error("❌ TextureLoader: FAILED to read file '{}'", path);
|
|
return result;
|
|
}
|
|
|
|
spdlog::info("✅ TextureLoader: File '{}' read successfully ({} bytes)", path, size);
|
|
auto loadResult = loadFromMemory(device, buffer.data(), buffer.size());
|
|
|
|
if (loadResult.success) {
|
|
spdlog::info("✅ TextureLoader: Texture '{}' loaded successfully ({}x{}, handle={})",
|
|
path, loadResult.width, loadResult.height, loadResult.handle.id);
|
|
} else {
|
|
spdlog::error("❌ TextureLoader: FAILED to load texture '{}': {}", path, loadResult.error);
|
|
}
|
|
|
|
return loadResult;
|
|
}
|
|
|
|
TextureLoader::LoadResult TextureLoader::loadFromMemory(rhi::IRHIDevice& device, const uint8_t* data, size_t size) {
|
|
LoadResult result;
|
|
|
|
// Decode image with stb_image
|
|
int width, height, channels;
|
|
// Force RGBA output (4 channels)
|
|
stbi_uc* pixels = stbi_load_from_memory(data, static_cast<int>(size), &width, &height, &channels, 4);
|
|
|
|
if (!pixels) {
|
|
result.error = "stb_image failed: ";
|
|
result.error += stbi_failure_reason();
|
|
return result;
|
|
}
|
|
|
|
// Log decoded image info
|
|
spdlog::info("TextureLoader: Decoded image {}x{} (original {} channels, converted to RGBA)",
|
|
width, height, channels);
|
|
if (width > 0 && height > 0) {
|
|
spdlog::info(" First pixel: R={}, G={}, B={}, A={}",
|
|
pixels[0], pixels[1], pixels[2], pixels[3]);
|
|
spdlog::info(" Last pixel: R={}, G={}, B={}, A={}",
|
|
pixels[(width*height-1)*4 + 0], pixels[(width*height-1)*4 + 1],
|
|
pixels[(width*height-1)*4 + 2], pixels[(width*height-1)*4 + 3]);
|
|
}
|
|
|
|
// Create texture via RHI
|
|
rhi::TextureDesc desc;
|
|
desc.width = static_cast<uint16_t>(width);
|
|
desc.height = static_cast<uint16_t>(height);
|
|
desc.format = rhi::TextureDesc::RGBA8;
|
|
desc.data = pixels;
|
|
desc.dataSize = static_cast<uint32_t>(width * height * 4);
|
|
|
|
result.handle = device.createTexture(desc);
|
|
result.width = desc.width;
|
|
result.height = desc.height;
|
|
result.success = result.handle.isValid();
|
|
|
|
if (result.success) {
|
|
spdlog::info("✅ TextureLoader: GPU texture created successfully (handle={})", result.handle.id);
|
|
} else {
|
|
result.error = "Failed to create GPU texture";
|
|
spdlog::error("❌ TextureLoader: FAILED to create GPU texture (handle invalid)");
|
|
}
|
|
|
|
// Free stb_image memory
|
|
stbi_image_free(pixels);
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace grove
|