1215 lines
42 KiB
C++
1215 lines
42 KiB
C++
#include "wiResourceManager.h"
|
|
#include "wiRenderer.h"
|
|
#include "wiHelper.h"
|
|
#include "wiTextureHelper.h"
|
|
#include "wiUnorderedMap.h"
|
|
#include "wiBacklog.h"
|
|
|
|
#include "Utility/stb_image.h"
|
|
#include "Utility/qoi.h"
|
|
#include "Utility/tinyddsloader.h"
|
|
#include "Utility/basis_universal/transcoder/basisu_transcoder.h"
|
|
extern basist::etc1_global_selector_codebook g_basis_global_codebook;
|
|
|
|
#include <algorithm>
|
|
#include <mutex>
|
|
|
|
using namespace wi::graphics;
|
|
|
|
namespace wi
|
|
{
|
|
struct ResourceInternal
|
|
{
|
|
resourcemanager::Flags flags = resourcemanager::Flags::NONE;
|
|
wi::graphics::Texture texture;
|
|
int srgb_subresource = -1;
|
|
wi::audio::Sound sound;
|
|
std::string script;
|
|
wi::video::Video video;
|
|
wi::vector<uint8_t> filedata;
|
|
};
|
|
|
|
const wi::vector<uint8_t>& Resource::GetFileData() const
|
|
{
|
|
const ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
return resourceinternal->filedata;
|
|
}
|
|
const wi::graphics::Texture& Resource::GetTexture() const
|
|
{
|
|
const ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
return resourceinternal->texture;
|
|
}
|
|
const wi::audio::Sound& Resource::GetSound() const
|
|
{
|
|
const ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
return resourceinternal->sound;
|
|
}
|
|
const std::string& Resource::GetScript() const
|
|
{
|
|
const ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
return resourceinternal->script;
|
|
}
|
|
const wi::video::Video& Resource::GetVideo() const
|
|
{
|
|
const ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
return resourceinternal->video;
|
|
}
|
|
int Resource::GetTextureSRGBSubresource() const
|
|
{
|
|
const ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
return resourceinternal->srgb_subresource;
|
|
}
|
|
|
|
void Resource::SetFileData(const wi::vector<uint8_t>& data)
|
|
{
|
|
if (internal_state == nullptr)
|
|
{
|
|
internal_state = std::make_shared<ResourceInternal>();
|
|
}
|
|
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
resourceinternal->filedata = data;
|
|
}
|
|
void Resource::SetFileData(wi::vector<uint8_t>&& data)
|
|
{
|
|
if (internal_state == nullptr)
|
|
{
|
|
internal_state = std::make_shared<ResourceInternal>();
|
|
}
|
|
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
resourceinternal->filedata = data;
|
|
}
|
|
void Resource::SetTexture(const wi::graphics::Texture& texture, int srgb_subresource)
|
|
{
|
|
if (internal_state == nullptr)
|
|
{
|
|
internal_state = std::make_shared<ResourceInternal>();
|
|
}
|
|
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
resourceinternal->texture = texture;
|
|
resourceinternal->srgb_subresource = srgb_subresource;
|
|
}
|
|
void Resource::SetSound(const wi::audio::Sound& sound)
|
|
{
|
|
if (internal_state == nullptr)
|
|
{
|
|
internal_state = std::make_shared<ResourceInternal>();
|
|
}
|
|
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
resourceinternal->sound = sound;
|
|
}
|
|
void Resource::SetScript(const std::string& script)
|
|
{
|
|
if (internal_state == nullptr)
|
|
{
|
|
internal_state = std::make_shared<ResourceInternal>();
|
|
}
|
|
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
resourceinternal->script = script;
|
|
}
|
|
void Resource::SetVideo(const wi::video::Video& video)
|
|
{
|
|
if (internal_state == nullptr)
|
|
{
|
|
internal_state = std::make_shared<ResourceInternal>();
|
|
}
|
|
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
resourceinternal->video = video;
|
|
}
|
|
|
|
void Resource::SetOutdated()
|
|
{
|
|
if (internal_state == nullptr)
|
|
{
|
|
internal_state = std::make_shared<ResourceInternal>();
|
|
}
|
|
ResourceInternal* resourceinternal = (ResourceInternal*)internal_state.get();
|
|
resourceinternal->flags |= resourcemanager::Flags::IMPORT_DELAY; // this will cause resource to be recreated, but let using old file data like delayed loading
|
|
}
|
|
|
|
namespace resourcemanager
|
|
{
|
|
static std::mutex locker;
|
|
static wi::unordered_map<std::string, std::weak_ptr<ResourceInternal>> resources;
|
|
static Mode mode = Mode::DISCARD_FILEDATA_AFTER_LOAD;
|
|
|
|
void SetMode(Mode param)
|
|
{
|
|
mode = param;
|
|
}
|
|
Mode GetMode()
|
|
{
|
|
return mode;
|
|
}
|
|
|
|
enum class DataType
|
|
{
|
|
IMAGE,
|
|
SOUND,
|
|
SCRIPT,
|
|
VIDEO,
|
|
};
|
|
static const wi::unordered_map<std::string, DataType> types = {
|
|
{"BASIS", DataType::IMAGE},
|
|
{"KTX2", DataType::IMAGE},
|
|
{"JPG", DataType::IMAGE},
|
|
{"JPEG", DataType::IMAGE},
|
|
{"PNG", DataType::IMAGE},
|
|
{"BMP", DataType::IMAGE},
|
|
{"DDS", DataType::IMAGE},
|
|
{"TGA", DataType::IMAGE},
|
|
{"QOI", DataType::IMAGE},
|
|
{"HDR", DataType::IMAGE},
|
|
{"WAV", DataType::SOUND},
|
|
{"OGG", DataType::SOUND},
|
|
{"LUA", DataType::SCRIPT},
|
|
{"MP4", DataType::VIDEO},
|
|
};
|
|
wi::vector<std::string> GetSupportedImageExtensions()
|
|
{
|
|
wi::vector<std::string> ret;
|
|
for (auto& x : types)
|
|
{
|
|
if (x.second == DataType::IMAGE)
|
|
{
|
|
ret.push_back(x.first);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
wi::vector<std::string> GetSupportedSoundExtensions()
|
|
{
|
|
wi::vector<std::string> ret;
|
|
for (auto& x : types)
|
|
{
|
|
if (x.second == DataType::SOUND)
|
|
{
|
|
ret.push_back(x.first);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
wi::vector<std::string> GetSupportedVideoExtensions()
|
|
{
|
|
wi::vector<std::string> ret;
|
|
for (auto& x : types)
|
|
{
|
|
if (x.second == DataType::VIDEO)
|
|
{
|
|
ret.push_back(x.first);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
wi::vector<std::string> GetSupportedScriptExtensions()
|
|
{
|
|
wi::vector<std::string> ret;
|
|
for (auto& x : types)
|
|
{
|
|
if (x.second == DataType::SCRIPT)
|
|
{
|
|
ret.push_back(x.first);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Resource Load(const std::string& name, Flags flags, const uint8_t* filedata, size_t filesize)
|
|
{
|
|
if (mode == Mode::DISCARD_FILEDATA_AFTER_LOAD)
|
|
{
|
|
flags &= ~Flags::IMPORT_RETAIN_FILEDATA;
|
|
}
|
|
|
|
locker.lock();
|
|
std::weak_ptr<ResourceInternal>& weak_resource = resources[name];
|
|
std::shared_ptr<ResourceInternal> resource = weak_resource.lock();
|
|
|
|
static bool basis_init = false; // within lock!
|
|
if (!basis_init)
|
|
{
|
|
basis_init = true;
|
|
basist::basisu_transcoder_init();
|
|
}
|
|
|
|
if (resource == nullptr)
|
|
{
|
|
resource = std::make_shared<ResourceInternal>();
|
|
resources[name] = resource;
|
|
}
|
|
else
|
|
{
|
|
if (!has_flag(flags, Flags::IMPORT_DELAY) && has_flag(resource->flags, Flags::IMPORT_DELAY))
|
|
{
|
|
// If this is not an IMPORT_DELAY load, but this resource load was incomplete, using IMPORT_DELAY,
|
|
// then continue loading it as normal from existing file data and remove IMPORT_DELAY flag from it
|
|
resource->flags &= ~Flags::IMPORT_DELAY;
|
|
}
|
|
else
|
|
{
|
|
Resource retVal;
|
|
retVal.internal_state = resource;
|
|
locker.unlock();
|
|
return retVal;
|
|
}
|
|
}
|
|
locker.unlock();
|
|
|
|
if (filedata == nullptr || filesize == 0)
|
|
{
|
|
if (resource->filedata.empty() && !wi::helper::FileRead(name, resource->filedata))
|
|
{
|
|
resource.reset();
|
|
return Resource();
|
|
}
|
|
filedata = resource->filedata.data();
|
|
filesize = resource->filedata.size();
|
|
}
|
|
|
|
bool success = false;
|
|
|
|
if (has_flag(flags, Flags::IMPORT_DELAY))
|
|
{
|
|
success = true;
|
|
}
|
|
else
|
|
{
|
|
std::string ext = wi::helper::toUpper(wi::helper::GetExtensionFromFileName(name));
|
|
DataType type;
|
|
|
|
// dynamic type selection:
|
|
{
|
|
auto it = types.find(ext);
|
|
if (it != types.end())
|
|
{
|
|
type = it->second;
|
|
}
|
|
else
|
|
{
|
|
return Resource();
|
|
}
|
|
}
|
|
|
|
switch (type)
|
|
{
|
|
case DataType::IMAGE:
|
|
{
|
|
GraphicsDevice* device = wi::graphics::GetDevice();
|
|
if (!ext.compare("KTX2"))
|
|
{
|
|
basist::ktx2_transcoder transcoder(&g_basis_global_codebook);
|
|
if (transcoder.init(filedata, (uint32_t)filesize))
|
|
{
|
|
TextureDesc desc;
|
|
desc.bind_flags = BindFlag::SHADER_RESOURCE;
|
|
desc.width = transcoder.get_width();
|
|
desc.height = transcoder.get_height();
|
|
desc.array_size = std::max(desc.array_size, transcoder.get_layers() * transcoder.get_faces());
|
|
desc.mip_levels = transcoder.get_levels();
|
|
desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING;
|
|
if (transcoder.get_faces() == 6)
|
|
{
|
|
desc.misc_flags |= ResourceMiscFlag::TEXTURECUBE;
|
|
}
|
|
|
|
basist::transcoder_texture_format fmt = basist::transcoder_texture_format::cTFRGBA32;
|
|
desc.format = Format::R8G8B8A8_UNORM;
|
|
|
|
bool import_compressed = has_flag(flags, Flags::IMPORT_BLOCK_COMPRESSED);
|
|
if (import_compressed)
|
|
{
|
|
// BC5 is disabled because it's missing green channel!
|
|
//if (has_flag(flags, Flags::IMPORT_NORMALMAP))
|
|
//{
|
|
// fmt = basist::transcoder_texture_format::cTFBC5_RG;
|
|
// desc.format = Format::BC5_UNORM;
|
|
// desc.swizzle.r = ComponentSwizzle::R;
|
|
// desc.swizzle.g = ComponentSwizzle::G;
|
|
// desc.swizzle.b = ComponentSwizzle::ONE;
|
|
// desc.swizzle.a = ComponentSwizzle::ONE;
|
|
//}
|
|
//else
|
|
{
|
|
if (transcoder.get_has_alpha())
|
|
{
|
|
fmt = basist::transcoder_texture_format::cTFBC3_RGBA;
|
|
desc.format = Format::BC3_UNORM;
|
|
}
|
|
else
|
|
{
|
|
fmt = basist::transcoder_texture_format::cTFBC1_RGB;
|
|
desc.format = Format::BC1_UNORM;
|
|
}
|
|
}
|
|
}
|
|
uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(fmt);
|
|
|
|
if (transcoder.start_transcoding())
|
|
{
|
|
// all subresources will use one allocation for transcoder destination, so compute combined size:
|
|
size_t transcoded_data_size = 0;
|
|
const uint32_t layers = std::max(1u, transcoder.get_layers());
|
|
const uint32_t faces = transcoder.get_faces();
|
|
const uint32_t levels = transcoder.get_levels();
|
|
for (uint32_t layer = 0; layer < layers; ++layer)
|
|
{
|
|
for (uint32_t face = 0; face < faces; ++face)
|
|
{
|
|
for (uint32_t mip = 0; mip < levels; ++mip)
|
|
{
|
|
basist::ktx2_image_level_info level_info;
|
|
if (transcoder.get_image_level_info(level_info, mip, layer, face))
|
|
{
|
|
uint32_t pixel_or_block_count = (import_compressed
|
|
? level_info.m_total_blocks
|
|
: (level_info.m_orig_width * level_info.m_orig_height));
|
|
transcoded_data_size += bytes_per_block * pixel_or_block_count;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
wi::vector<uint8_t> transcoded_data(transcoded_data_size);
|
|
|
|
wi::vector<SubresourceData> InitData;
|
|
size_t transcoded_data_offset = 0;
|
|
for (uint32_t layer = 0; layer < layers; ++layer)
|
|
{
|
|
for (uint32_t face = 0; face < faces; ++face)
|
|
{
|
|
for (uint32_t mip = 0; mip < levels; ++mip)
|
|
{
|
|
basist::ktx2_image_level_info level_info;
|
|
if (transcoder.get_image_level_info(level_info, mip, layer, face))
|
|
{
|
|
void* data_ptr = transcoded_data.data() + transcoded_data_offset;
|
|
uint32_t pixel_or_block_count = (import_compressed
|
|
? level_info.m_total_blocks
|
|
: (level_info.m_orig_width * level_info.m_orig_height));
|
|
transcoded_data_offset += bytes_per_block * pixel_or_block_count;
|
|
if (transcoder.transcode_image_level(
|
|
mip,
|
|
layer,
|
|
face,
|
|
data_ptr,
|
|
pixel_or_block_count,
|
|
fmt
|
|
))
|
|
{
|
|
SubresourceData subresourceData;
|
|
subresourceData.data_ptr = data_ptr;
|
|
subresourceData.row_pitch = (import_compressed ? level_info.m_num_blocks_x : level_info.m_orig_width) * bytes_per_block;
|
|
subresourceData.slice_pitch = subresourceData.row_pitch * (import_compressed ? level_info.m_num_blocks_y : level_info.m_orig_height);
|
|
InitData.push_back(subresourceData);
|
|
}
|
|
else
|
|
{
|
|
wi::backlog::post("KTX2 transcoding error while loading image!", wi::backlog::LogLevel::Error);
|
|
assert(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wi::backlog::post("KTX2 transcoding error while loading image level info!", wi::backlog::LogLevel::Error);
|
|
assert(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!InitData.empty())
|
|
{
|
|
success = device->CreateTexture(&desc, InitData.data(), &resource->texture);
|
|
device->SetName(&resource->texture, name.c_str());
|
|
|
|
Format srgb_format = GetFormatSRGB(desc.format);
|
|
if (srgb_format != Format::UNKNOWN && srgb_format != desc.format)
|
|
{
|
|
resource->srgb_subresource = device->CreateSubresource(
|
|
&resource->texture,
|
|
SubresourceType::SRV,
|
|
0, -1,
|
|
0, -1,
|
|
&srgb_format
|
|
);
|
|
}
|
|
}
|
|
}
|
|
transcoder.clear();
|
|
}
|
|
}
|
|
else if (!ext.compare("BASIS"))
|
|
{
|
|
basist::basisu_transcoder transcoder(&g_basis_global_codebook);
|
|
if (transcoder.validate_header(filedata, (uint32_t)filesize))
|
|
{
|
|
basist::basisu_file_info fileInfo;
|
|
if (transcoder.get_file_info(filedata, (uint32_t)filesize, fileInfo))
|
|
{
|
|
uint32_t image_index = 0;
|
|
basist::basisu_image_info info;
|
|
if (transcoder.get_image_info(filedata, (uint32_t)filesize, info, image_index))
|
|
{
|
|
TextureDesc desc;
|
|
desc.bind_flags = BindFlag::SHADER_RESOURCE;
|
|
desc.width = info.m_width;
|
|
desc.height = info.m_height;
|
|
desc.mip_levels = info.m_total_levels;
|
|
desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING;
|
|
|
|
basist::transcoder_texture_format fmt = basist::transcoder_texture_format::cTFRGBA32;
|
|
desc.format = Format::R8G8B8A8_UNORM;
|
|
|
|
bool import_compressed = has_flag(flags, Flags::IMPORT_BLOCK_COMPRESSED);
|
|
if (import_compressed)
|
|
{
|
|
// BC5 is disabled because it's missing green channel!
|
|
//if (has_flag(flags, Flags::IMPORT_NORMALMAP))
|
|
//{
|
|
// fmt = basist::transcoder_texture_format::cTFBC5_RG;
|
|
// desc.format = Format::BC5_UNORM;
|
|
// desc.swizzle.r = ComponentSwizzle::R;
|
|
// desc.swizzle.g = ComponentSwizzle::G;
|
|
// desc.swizzle.b = ComponentSwizzle::ONE;
|
|
// desc.swizzle.a = ComponentSwizzle::ONE;
|
|
//}
|
|
//else
|
|
{
|
|
if (info.m_alpha_flag)
|
|
{
|
|
fmt = basist::transcoder_texture_format::cTFBC3_RGBA;
|
|
desc.format = Format::BC3_UNORM;
|
|
}
|
|
else
|
|
{
|
|
fmt = basist::transcoder_texture_format::cTFBC1_RGB;
|
|
desc.format = Format::BC1_UNORM;
|
|
}
|
|
}
|
|
}
|
|
uint32_t bytes_per_block = basis_get_bytes_per_block_or_pixel(fmt);
|
|
|
|
if (transcoder.start_transcoding(filedata, (uint32_t)filesize))
|
|
{
|
|
// all subresources will use one allocation for transcoder destination, so compute combined size:
|
|
size_t transcoded_data_size = 0;
|
|
for (uint32_t mip = 0; mip < desc.mip_levels; ++mip)
|
|
{
|
|
basist::basisu_image_level_info level_info;
|
|
if (transcoder.get_image_level_info(filedata, (uint32_t)filesize, level_info, image_index, mip))
|
|
{
|
|
uint32_t pixel_or_block_count = (import_compressed
|
|
? level_info.m_total_blocks
|
|
: (level_info.m_orig_width * level_info.m_orig_height));
|
|
transcoded_data_size += bytes_per_block * pixel_or_block_count;
|
|
}
|
|
}
|
|
wi::vector<uint8_t> transcoded_data(transcoded_data_size);
|
|
|
|
wi::vector<SubresourceData> InitData;
|
|
size_t transcoded_data_offset = 0;
|
|
for (uint32_t mip = 0; mip < desc.mip_levels; ++mip)
|
|
{
|
|
basist::basisu_image_level_info level_info;
|
|
if (transcoder.get_image_level_info(filedata, (uint32_t)filesize, level_info, 0, mip))
|
|
{
|
|
void* data_ptr = transcoded_data.data() + transcoded_data_offset;
|
|
uint32_t pixel_or_block_count = (import_compressed
|
|
? level_info.m_total_blocks
|
|
: (level_info.m_orig_width * level_info.m_orig_height));
|
|
transcoded_data_offset += pixel_or_block_count * bytes_per_block;
|
|
if (transcoder.transcode_image_level(
|
|
filedata,
|
|
(uint32_t)filesize,
|
|
image_index,
|
|
mip,
|
|
data_ptr,
|
|
pixel_or_block_count,
|
|
fmt
|
|
))
|
|
{
|
|
SubresourceData subresourceData;
|
|
subresourceData.data_ptr = data_ptr;
|
|
subresourceData.row_pitch = (import_compressed ? level_info.m_num_blocks_x : level_info.m_orig_width) * bytes_per_block;
|
|
subresourceData.slice_pitch = subresourceData.row_pitch * (import_compressed ? level_info.m_num_blocks_y : level_info.m_orig_height);
|
|
InitData.push_back(subresourceData);
|
|
}
|
|
else
|
|
{
|
|
wi::backlog::post("BASIS transcoding error while loading image!", wi::backlog::LogLevel::Error);
|
|
assert(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wi::backlog::post("BASIS transcoding error while loading image level info!", wi::backlog::LogLevel::Error);
|
|
assert(0);
|
|
}
|
|
}
|
|
|
|
if (!InitData.empty())
|
|
{
|
|
success = device->CreateTexture(&desc, InitData.data(), &resource->texture);
|
|
device->SetName(&resource->texture, name.c_str());
|
|
|
|
Format srgb_format = GetFormatSRGB(desc.format);
|
|
if (srgb_format != Format::UNKNOWN && srgb_format != desc.format)
|
|
{
|
|
resource->srgb_subresource = device->CreateSubresource(
|
|
&resource->texture,
|
|
SubresourceType::SRV,
|
|
0, -1,
|
|
0, -1,
|
|
&srgb_format
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!ext.compare("DDS"))
|
|
{
|
|
// Load dds
|
|
|
|
tinyddsloader::DDSFile dds;
|
|
auto result = dds.Load(filedata, filesize);
|
|
|
|
if (result == tinyddsloader::Result::Success)
|
|
{
|
|
TextureDesc desc;
|
|
desc.array_size = 1;
|
|
desc.bind_flags = BindFlag::SHADER_RESOURCE;
|
|
desc.width = dds.GetWidth();
|
|
desc.height = dds.GetHeight();
|
|
desc.depth = dds.GetDepth();
|
|
desc.mip_levels = dds.GetMipCount();
|
|
desc.array_size = dds.GetArraySize();
|
|
desc.format = Format::R8G8B8A8_UNORM;
|
|
desc.layout = ResourceState::SHADER_RESOURCE;
|
|
desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING;
|
|
|
|
if (dds.IsCubemap())
|
|
{
|
|
desc.misc_flags |= ResourceMiscFlag::TEXTURECUBE;
|
|
}
|
|
|
|
auto ddsFormat = dds.GetFormat();
|
|
|
|
switch (ddsFormat)
|
|
{
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32G32B32A32_Float: desc.format = Format::R32G32B32A32_FLOAT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32G32B32A32_UInt: desc.format = Format::R32G32B32A32_UINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32G32B32A32_SInt: desc.format = Format::R32G32B32A32_SINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32G32B32_Float: desc.format = Format::R32G32B32_FLOAT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32G32B32_UInt: desc.format = Format::R32G32B32_UINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32G32B32_SInt: desc.format = Format::R32G32B32_SINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_Float: desc.format = Format::R16G16B16A16_FLOAT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_UNorm: desc.format = Format::R16G16B16A16_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_UInt: desc.format = Format::R16G16B16A16_UINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_SNorm: desc.format = Format::R16G16B16A16_SNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16G16B16A16_SInt: desc.format = Format::R16G16B16A16_SINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32G32_Float: desc.format = Format::R32G32_FLOAT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32G32_UInt: desc.format = Format::R32G32_UINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32G32_SInt: desc.format = Format::R32G32_SINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R10G10B10A2_UNorm: desc.format = Format::R10G10B10A2_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R10G10B10A2_UInt: desc.format = Format::R10G10B10A2_UINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R11G11B10_Float: desc.format = Format::R11G11B10_FLOAT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::B8G8R8X8_UNorm: desc.format = Format::B8G8R8A8_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::B8G8R8A8_UNorm: desc.format = Format::B8G8R8A8_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::B8G8R8A8_UNorm_SRGB: desc.format = Format::B8G8R8A8_UNORM_SRGB; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UNorm: desc.format = Format::R8G8B8A8_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UNorm_SRGB: desc.format = Format::R8G8B8A8_UNORM_SRGB; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_UInt: desc.format = Format::R8G8B8A8_UINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_SNorm: desc.format = Format::R8G8B8A8_SNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8G8B8A8_SInt: desc.format = Format::R8G8B8A8_SINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16G16_Float: desc.format = Format::R16G16_FLOAT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16G16_UNorm: desc.format = Format::R16G16_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16G16_UInt: desc.format = Format::R16G16_UINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16G16_SNorm: desc.format = Format::R16G16_SNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16G16_SInt: desc.format = Format::R16G16_SINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::D32_Float: desc.format = Format::D32_FLOAT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32_Float: desc.format = Format::R32_FLOAT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32_UInt: desc.format = Format::R32_UINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R32_SInt: desc.format = Format::R32_SINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8G8_UNorm: desc.format = Format::R8G8_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8G8_UInt: desc.format = Format::R8G8_UINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8G8_SNorm: desc.format = Format::R8G8_SNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8G8_SInt: desc.format = Format::R8G8_SINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16_Float: desc.format = Format::R16_FLOAT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::D16_UNorm: desc.format = Format::D16_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16_UNorm: desc.format = Format::R16_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16_UInt: desc.format = Format::R16_UINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16_SNorm: desc.format = Format::R16_SNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R16_SInt: desc.format = Format::R16_SINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8_UNorm: desc.format = Format::R8_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8_UInt: desc.format = Format::R8_UINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8_SNorm: desc.format = Format::R8_SNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::R8_SInt: desc.format = Format::R8_SINT; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC1_UNorm: desc.format = Format::BC1_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC1_UNorm_SRGB: desc.format = Format::BC1_UNORM_SRGB; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC2_UNorm: desc.format = Format::BC2_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC2_UNorm_SRGB: desc.format = Format::BC2_UNORM_SRGB; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC3_UNorm: desc.format = Format::BC3_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC3_UNorm_SRGB: desc.format = Format::BC3_UNORM_SRGB; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC4_UNorm: desc.format = Format::BC4_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC4_SNorm: desc.format = Format::BC4_SNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC5_UNorm: desc.format = Format::BC5_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC5_SNorm: desc.format = Format::BC5_SNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC6H_SF16: desc.format = Format::BC6H_SF16; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC6H_UF16: desc.format = Format::BC6H_UF16; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC7_UNorm: desc.format = Format::BC7_UNORM; break;
|
|
case tinyddsloader::DDSFile::DXGIFormat::BC7_UNorm_SRGB: desc.format = Format::BC7_UNORM_SRGB; break;
|
|
default:
|
|
assert(0); // incoming format is not supported
|
|
break;
|
|
}
|
|
|
|
if (desc.format == Format::BC5_UNORM)
|
|
{
|
|
desc.swizzle.r = ComponentSwizzle::R;
|
|
desc.swizzle.g = ComponentSwizzle::G;
|
|
desc.swizzle.b = ComponentSwizzle::ONE;
|
|
desc.swizzle.a = ComponentSwizzle::ONE;
|
|
}
|
|
|
|
wi::vector<SubresourceData> InitData;
|
|
for (uint32_t arrayIndex = 0; arrayIndex < desc.array_size; ++arrayIndex)
|
|
{
|
|
for (uint32_t mip = 0; mip < desc.mip_levels; ++mip)
|
|
{
|
|
auto imageData = dds.GetImageData(mip, arrayIndex);
|
|
SubresourceData subresourceData;
|
|
subresourceData.data_ptr = imageData->m_mem;
|
|
subresourceData.row_pitch = imageData->m_memPitch;
|
|
subresourceData.slice_pitch = imageData->m_memSlicePitch;
|
|
InitData.push_back(subresourceData);
|
|
}
|
|
}
|
|
|
|
auto dim = dds.GetTextureDimension();
|
|
switch (dim)
|
|
{
|
|
case tinyddsloader::DDSFile::TextureDimension::Texture1D:
|
|
{
|
|
desc.type = TextureDesc::Type::TEXTURE_1D;
|
|
}
|
|
break;
|
|
case tinyddsloader::DDSFile::TextureDimension::Texture2D:
|
|
{
|
|
desc.type = TextureDesc::Type::TEXTURE_2D;
|
|
}
|
|
break;
|
|
case tinyddsloader::DDSFile::TextureDimension::Texture3D:
|
|
{
|
|
desc.type = TextureDesc::Type::TEXTURE_3D;
|
|
}
|
|
break;
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
if (IsFormatBlockCompressed(desc.format))
|
|
{
|
|
desc.width = AlignTo(desc.width, GetFormatBlockSize(desc.format));
|
|
desc.height = AlignTo(desc.height, GetFormatBlockSize(desc.format));
|
|
}
|
|
|
|
success = device->CreateTexture(&desc, InitData.data(), &resource->texture);
|
|
device->SetName(&resource->texture, name.c_str());
|
|
|
|
Format srgb_format = GetFormatSRGB(desc.format);
|
|
if (srgb_format != Format::UNKNOWN && srgb_format != desc.format)
|
|
{
|
|
resource->srgb_subresource = device->CreateSubresource(
|
|
&resource->texture,
|
|
SubresourceType::SRV,
|
|
0, -1,
|
|
0, -1,
|
|
&srgb_format
|
|
);
|
|
}
|
|
}
|
|
else assert(0); // failed to load DDS
|
|
|
|
}
|
|
else if (!ext.compare("HDR"))
|
|
{
|
|
int height, width, channels; // stb_image
|
|
float* data = stbi_loadf_from_memory(filedata, (int)filesize, &width, &height, &channels, 0);
|
|
static constexpr bool allow_packing = true; // we now always assume that we won't need full precision float textures, so pack them for memory saving
|
|
|
|
if (data != nullptr)
|
|
{
|
|
TextureDesc desc;
|
|
desc.width = (uint32_t)width;
|
|
desc.height = (uint32_t)height;
|
|
switch (channels)
|
|
{
|
|
default:
|
|
case 4:
|
|
if (allow_packing)
|
|
{
|
|
desc.format = Format::R16G16B16A16_FLOAT;
|
|
const XMFLOAT4* data_full = (const XMFLOAT4*)data;
|
|
XMHALF4* data_packed = (XMHALF4*)data;
|
|
for (int i = 0; i < width * height; ++i)
|
|
{
|
|
XMStoreHalf4(data_packed + i, XMLoadFloat4(data_full + i));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
desc.format = Format::R32G32B32A32_FLOAT;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (allow_packing)
|
|
{
|
|
desc.format = Format::R9G9B9E5_SHAREDEXP;
|
|
const XMFLOAT3* data_full = (const XMFLOAT3*)data;
|
|
XMFLOAT3SE* data_packed = (XMFLOAT3SE*)data;
|
|
for (int i = 0; i < width * height; ++i)
|
|
{
|
|
XMStoreFloat3SE(data_packed + i, XMLoadFloat3(data_full + i));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
desc.format = Format::R32G32B32_FLOAT;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (allow_packing)
|
|
{
|
|
desc.format = Format::R16G16_FLOAT;
|
|
const XMFLOAT2* data_full = (const XMFLOAT2*)data;
|
|
XMHALF2* data_packed = (XMHALF2*)data;
|
|
for (int i = 0; i < width * height; ++i)
|
|
{
|
|
XMStoreHalf2(data_packed + i, XMLoadFloat2(data_full + i));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
desc.format = Format::R32G32_FLOAT;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (allow_packing)
|
|
{
|
|
desc.format = Format::R16_FLOAT;
|
|
HALF* data_packed = (HALF*)data;
|
|
for (int i = 0; i < width * height; ++i)
|
|
{
|
|
data_packed[i] = XMConvertFloatToHalf(data[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
desc.format = Format::R32_FLOAT;
|
|
}
|
|
break;
|
|
}
|
|
desc.bind_flags = BindFlag::SHADER_RESOURCE;
|
|
desc.mip_levels = 1;
|
|
SubresourceData InitData;
|
|
InitData.data_ptr = data;
|
|
InitData.row_pitch = width * GetFormatStride(desc.format);
|
|
success = device->CreateTexture(&desc, &InitData, &resource->texture);
|
|
device->SetName(&resource->texture, name.c_str());
|
|
|
|
stbi_image_free(data);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// qoi, png, tga, jpg, etc. loader:
|
|
const int channelCount = 4;
|
|
int height = 0, width = 0, bpp = 0;
|
|
bool is_16bit = false;
|
|
|
|
void* rgba;
|
|
if (!ext.compare("QOI"))
|
|
{
|
|
qoi_desc desc = {};
|
|
rgba = qoi_decode(filedata, (int)filesize, &desc, channelCount);
|
|
// redefine width, height to avoid further conditionals
|
|
height = desc.height;
|
|
width = desc.width;
|
|
}
|
|
else
|
|
{
|
|
if (!has_flag(flags, Flags::IMPORT_COLORGRADINGLUT) && stbi_is_16_bit_from_memory(filedata, (int)filesize))
|
|
{
|
|
is_16bit = true;
|
|
rgba = stbi_load_16_from_memory(filedata, (int)filesize, &width, &height, &bpp, channelCount);
|
|
}
|
|
else
|
|
{
|
|
rgba = stbi_load_from_memory(filedata, (int)filesize, &width, &height, &bpp, channelCount);
|
|
}
|
|
}
|
|
|
|
if (rgba != nullptr)
|
|
{
|
|
TextureDesc desc;
|
|
desc.height = uint32_t(height);
|
|
desc.width = uint32_t(width);
|
|
desc.layout = ResourceState::SHADER_RESOURCE;
|
|
|
|
if (has_flag(flags, Flags::IMPORT_COLORGRADINGLUT))
|
|
{
|
|
if (desc.type != TextureDesc::Type::TEXTURE_2D ||
|
|
desc.width != 256 ||
|
|
desc.height != 16)
|
|
{
|
|
wi::helper::messageBox("The Dimensions must be 256 x 16 for color grading LUT!", "Error");
|
|
}
|
|
else
|
|
{
|
|
uint32_t data[16 * 16 * 16];
|
|
int pixel = 0;
|
|
for (int z = 0; z < 16; ++z)
|
|
{
|
|
for (int y = 0; y < 16; ++y)
|
|
{
|
|
for (int x = 0; x < 16; ++x)
|
|
{
|
|
int coord = x + y * 256 + z * 16;
|
|
data[pixel++] = ((uint32_t*)rgba)[coord];
|
|
}
|
|
}
|
|
}
|
|
|
|
desc.type = TextureDesc::Type::TEXTURE_3D;
|
|
desc.width = 16;
|
|
desc.height = 16;
|
|
desc.depth = 16;
|
|
desc.format = Format::R8G8B8A8_UNORM;
|
|
desc.bind_flags = BindFlag::SHADER_RESOURCE;
|
|
SubresourceData InitData;
|
|
InitData.data_ptr = data;
|
|
InitData.row_pitch = 16 * sizeof(uint32_t);
|
|
InitData.slice_pitch = 16 * InitData.row_pitch;
|
|
success = device->CreateTexture(&desc, &InitData, &resource->texture);
|
|
device->SetName(&resource->texture, name.c_str());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
desc.bind_flags = BindFlag::SHADER_RESOURCE | BindFlag::UNORDERED_ACCESS;
|
|
if (is_16bit)
|
|
{
|
|
desc.format = Format::R16G16B16A16_UNORM;
|
|
}
|
|
else
|
|
{
|
|
desc.format = Format::R8G8B8A8_UNORM;
|
|
}
|
|
desc.mip_levels = GetMipCount(desc.width, desc.height);
|
|
desc.usage = Usage::DEFAULT;
|
|
desc.layout = ResourceState::SHADER_RESOURCE;
|
|
desc.misc_flags = ResourceMiscFlag::TYPED_FORMAT_CASTING;
|
|
|
|
uint32_t mipwidth = width;
|
|
SubresourceData init_data[32]; // don't support more than 32 mips anyway
|
|
for (uint32_t mip = 0; mip < desc.mip_levels; ++mip)
|
|
{
|
|
init_data[mip].data_ptr = rgba; // attention! we don't fill the mips here correctly, just always point to the mip0 data by default. Mip levels will be created using compute shader when needed!
|
|
init_data[mip].row_pitch = uint32_t(mipwidth * GetFormatStride(desc.format));
|
|
mipwidth = std::max(1u, mipwidth / 2);
|
|
}
|
|
|
|
success = device->CreateTexture(&desc, init_data, &resource->texture);
|
|
device->SetName(&resource->texture, name.c_str());
|
|
|
|
for (uint32_t i = 0; i < resource->texture.desc.mip_levels; ++i)
|
|
{
|
|
int subresource_index;
|
|
subresource_index = device->CreateSubresource(&resource->texture, SubresourceType::SRV, 0, 1, i, 1);
|
|
assert(subresource_index == i);
|
|
subresource_index = device->CreateSubresource(&resource->texture, SubresourceType::UAV, 0, 1, i, 1);
|
|
assert(subresource_index == i);
|
|
}
|
|
|
|
// This part must be AFTER mip level subresource creation:
|
|
Format srgb_format = GetFormatSRGB(desc.format);
|
|
if (srgb_format != Format::UNKNOWN && srgb_format != desc.format)
|
|
{
|
|
resource->srgb_subresource = device->CreateSubresource(
|
|
&resource->texture,
|
|
SubresourceType::SRV,
|
|
0, -1,
|
|
0, -1,
|
|
&srgb_format
|
|
);
|
|
}
|
|
|
|
wi::renderer::AddDeferredMIPGen(resource->texture, true);
|
|
|
|
if (has_flag(flags, Flags::IMPORT_BLOCK_COMPRESSED))
|
|
{
|
|
// Schedule additional task to compress into BC format and replace resource texture:
|
|
Texture uncompressed_src = std::move(resource->texture);
|
|
resource->srgb_subresource = -1;
|
|
|
|
desc.format = Format::BC1_UNORM;
|
|
if (has_flag(flags, Flags::IMPORT_NORMALMAP))
|
|
{
|
|
desc.format = Format::BC5_UNORM;
|
|
desc.swizzle.r = ComponentSwizzle::R;
|
|
desc.swizzle.g = ComponentSwizzle::G;
|
|
desc.swizzle.b = ComponentSwizzle::ONE;
|
|
desc.swizzle.a = ComponentSwizzle::ONE;
|
|
}
|
|
else
|
|
{
|
|
// scan for transparency and also check if fully grayscale:
|
|
// By default we should use BC1 that doesn't have transparency, but half the size of BC3 that supports it
|
|
// We only care about grayscale if it's not transparent
|
|
bool has_transparency = false;
|
|
bool is_grayscale = true;
|
|
if (is_16bit)
|
|
{
|
|
for (int y = 0; (y < height) && !has_transparency; ++y)
|
|
{
|
|
for (int x = 0; (x < width) && !has_transparency; ++x)
|
|
{
|
|
const wi::Color16 color = ((wi::Color16*)rgba)[x + y * width];
|
|
const uint16_t r = color.getR();
|
|
const uint16_t g = color.getG();
|
|
const uint16_t b = color.getB();
|
|
const uint16_t a = color.getA();
|
|
has_transparency |= a < 65535;
|
|
is_grayscale &= r == g;
|
|
is_grayscale &= r == b;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int y = 0; (y < height) && !has_transparency; ++y)
|
|
{
|
|
for (int x = 0; (x < width) && !has_transparency; ++x)
|
|
{
|
|
const wi::Color color = ((wi::Color*)rgba)[x + y * width];
|
|
const uint8_t r = color.getR();
|
|
const uint8_t g = color.getG();
|
|
const uint8_t b = color.getB();
|
|
const uint8_t a = color.getA();
|
|
has_transparency |= a < 255;
|
|
is_grayscale &= r == g;
|
|
is_grayscale &= r == b;
|
|
}
|
|
}
|
|
}
|
|
if (has_transparency)
|
|
{
|
|
// BC3 format supports transparency:
|
|
desc.format = Format::BC3_UNORM;
|
|
}
|
|
else if (is_grayscale)
|
|
{
|
|
// If not transparent and grayscale, than BC4 is better quality than BC1 with same memory footprint
|
|
desc.format = Format::BC4_UNORM;
|
|
// In this case, reswizzle the texture to be grayscale, not red. Red is ok for some maps, but not all, it's better to use all channels, for example grayscale specular map
|
|
desc.swizzle.r = ComponentSwizzle::R;
|
|
desc.swizzle.g = ComponentSwizzle::R;
|
|
desc.swizzle.b = ComponentSwizzle::R;
|
|
desc.swizzle.a = ComponentSwizzle::ONE;
|
|
}
|
|
}
|
|
desc.bind_flags = BindFlag::SHADER_RESOURCE;
|
|
|
|
const uint32_t block_size = GetFormatBlockSize(desc.format);
|
|
desc.width = AlignTo(desc.width, block_size);
|
|
desc.height = AlignTo(desc.height, block_size);
|
|
desc.mip_levels = GetMipCount(desc.width, desc.height, desc.depth, block_size, block_size);
|
|
|
|
success = device->CreateTexture(&desc, nullptr, &resource->texture);
|
|
device->SetName(&resource->texture, name.c_str());
|
|
|
|
// This part must be AFTER mip level subresource creation:
|
|
Format srgb_format = GetFormatSRGB(desc.format);
|
|
if (srgb_format != Format::UNKNOWN && srgb_format != desc.format)
|
|
{
|
|
resource->srgb_subresource = device->CreateSubresource(
|
|
&resource->texture,
|
|
SubresourceType::SRV,
|
|
0, -1,
|
|
0, -1,
|
|
&srgb_format
|
|
);
|
|
}
|
|
|
|
wi::renderer::AddDeferredBlockCompression(uncompressed_src, resource->texture);
|
|
}
|
|
}
|
|
}
|
|
free(rgba);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DataType::SOUND:
|
|
{
|
|
success = wi::audio::CreateSound(filedata, filesize, &resource->sound);
|
|
}
|
|
break;
|
|
|
|
case DataType::SCRIPT:
|
|
{
|
|
resource->script.resize(filesize);
|
|
std::memcpy(resource->script.data(), filedata, filesize);
|
|
success = true;
|
|
}
|
|
break;
|
|
|
|
case DataType::VIDEO:
|
|
{
|
|
success = wi::video::CreateVideo(filedata, filesize, &resource->video);
|
|
}
|
|
break;
|
|
|
|
};
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
resource->flags = flags;
|
|
|
|
if (resource->filedata.empty() && (has_flag(flags, Flags::IMPORT_RETAIN_FILEDATA) || has_flag(flags, Flags::IMPORT_DELAY)))
|
|
{
|
|
// resource was loaded with external filedata, and we want to retain filedata
|
|
// this must also happen when using IMPORT_DELAY!
|
|
resource->filedata.resize(filesize);
|
|
std::memcpy(resource->filedata.data(), filedata, filesize);
|
|
}
|
|
else if (!resource->filedata.empty() && has_flag(flags, Flags::IMPORT_RETAIN_FILEDATA) == 0)
|
|
{
|
|
// resource was loaded using file name, and we want to discard filedata
|
|
resource->filedata.clear();
|
|
}
|
|
|
|
Resource retVal;
|
|
retVal.internal_state = resource;
|
|
return retVal;
|
|
}
|
|
|
|
return Resource();
|
|
}
|
|
|
|
bool Contains(const std::string& name)
|
|
{
|
|
bool result = false;
|
|
locker.lock();
|
|
auto it = resources.find(name);
|
|
if (it != resources.end())
|
|
{
|
|
auto resource = it->second.lock();
|
|
result = resource != nullptr;
|
|
}
|
|
locker.unlock();
|
|
return result;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
locker.lock();
|
|
resources.clear();
|
|
locker.unlock();
|
|
}
|
|
|
|
|
|
void Serialize(wi::Archive& archive, ResourceSerializer& seri)
|
|
{
|
|
if (archive.IsReadMode())
|
|
{
|
|
size_t serializable_count = 0;
|
|
archive >> serializable_count;
|
|
|
|
struct TempResource
|
|
{
|
|
std::string name;
|
|
Flags flags = Flags::NONE;
|
|
wi::vector<uint8_t> filedata;
|
|
};
|
|
wi::vector<TempResource> temp_resources;
|
|
temp_resources.resize(serializable_count);
|
|
|
|
wi::jobsystem::context ctx;
|
|
std::mutex seri_locker;
|
|
for (size_t i = 0; i < serializable_count; ++i)
|
|
{
|
|
auto& resource = temp_resources[i];
|
|
|
|
archive >> resource.name;
|
|
uint32_t flags_temp;
|
|
archive >> flags_temp;
|
|
resource.flags = (Flags)flags_temp;
|
|
archive >> resource.filedata;
|
|
|
|
resource.name = archive.GetSourceDirectory() + resource.name;
|
|
resource.flags |= Flags::IMPORT_DELAY; // delay resource creation, to be able to receive additional flags (this way only file data is loaded)
|
|
|
|
// "Loading" the resource can happen asynchronously to serialization of file data, to improve performance
|
|
wi::jobsystem::Execute(ctx, [i, &temp_resources, &seri_locker, &seri](wi::jobsystem::JobArgs args) {
|
|
auto& tmp_resource = temp_resources[i];
|
|
auto res = Load(tmp_resource.name, tmp_resource.flags, tmp_resource.filedata.data(), tmp_resource.filedata.size());
|
|
seri_locker.lock();
|
|
seri.resources.push_back(res);
|
|
seri_locker.unlock();
|
|
});
|
|
}
|
|
|
|
wi::jobsystem::Wait(ctx);
|
|
}
|
|
else
|
|
{
|
|
locker.lock();
|
|
size_t serializable_count = 0;
|
|
|
|
if (mode == Mode::ALLOW_RETAIN_FILEDATA_BUT_DISABLE_EMBEDDING)
|
|
{
|
|
// Simply not serialize any embedded resources
|
|
serializable_count = 0;
|
|
archive << serializable_count;
|
|
}
|
|
else
|
|
{
|
|
// Count embedded resources:
|
|
for (auto& it : resources)
|
|
{
|
|
std::shared_ptr<ResourceInternal> resource = it.second.lock();
|
|
if (resource != nullptr && !resource->filedata.empty())
|
|
{
|
|
serializable_count++;
|
|
}
|
|
}
|
|
|
|
// Write all embedded resources:
|
|
archive << serializable_count;
|
|
for (auto& it : resources)
|
|
{
|
|
std::shared_ptr<ResourceInternal> resource = it.second.lock();
|
|
|
|
if (resource != nullptr && !resource->filedata.empty())
|
|
{
|
|
std::string name = it.first;
|
|
wi::helper::MakePathRelative(archive.GetSourceDirectory(), name);
|
|
|
|
archive << name;
|
|
archive << (uint32_t)resource->flags;
|
|
archive << resource->filedata;
|
|
}
|
|
}
|
|
}
|
|
locker.unlock();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|