library: first impl of load_async

This commit is contained in:
2018-12-21 18:37:54 +07:00
parent d88f0db608
commit c5b55cc620
16 changed files with 984 additions and 683 deletions

View File

@@ -60,7 +60,7 @@ if(E2D_BUILD_WITH_SANITIZER AND (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${E2D_SANITIZER_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${E2D_SANITIZER_FLAGS}")
set(CMAKE_LINKER_FLAGS_DEBUG "${CMAKE_LINKER_FLAGS_DEBUG} ${E2D_SANITIZER_FLAGS}")
endif(E2D_BUILD_WITH_SANITIZER)
endif()
#
# e2d sources

View File

@@ -11,58 +11,67 @@
namespace e2d
{
template < typename T >
class asset_loading_exception : public exception {
const char* what() const noexcept override = 0;
};
template < typename Asset, typename Content >
class content_asset : public asset {
public:
content_asset(T content)
using ptr = std::shared_ptr<Asset>;
using load_result = std::shared_ptr<Asset>;
using load_async_result = stdex::promise<load_result>;
public:
content_asset(Content content)
: content_(std::move(content)) {}
const T& content() const noexcept {
const Content& content() const noexcept {
return content_;
}
private:
T content_;
Content content_;
};
class text_asset final : public content_asset<str> {
class text_asset final : public content_asset<text_asset, str> {
public:
using content_asset<str>::content_asset;
static std::shared_ptr<text_asset> load(library& library, str_view address);
using content_asset<text_asset, str>::content_asset;
static load_async_result load_async(library& library, str_view address);
};
class mesh_asset final : public content_asset<mesh> {
class mesh_asset final : public content_asset<mesh_asset, mesh> {
public:
using content_asset<mesh>::content_asset;
static std::shared_ptr<mesh_asset> load(library& library, str_view address);
using content_asset<mesh_asset, mesh>::content_asset;
static load_async_result load_async(library& library, str_view address);
};
class image_asset final : public content_asset<image> {
class image_asset final : public content_asset<image_asset, image> {
public:
using content_asset<image>::content_asset;
static std::shared_ptr<image_asset> load(library& library, str_view address);
using content_asset<image_asset, image>::content_asset;
static load_async_result load_async(library& library, str_view address);
};
class binary_asset final : public content_asset<buffer> {
class binary_asset final : public content_asset<binary_asset, buffer> {
public:
using content_asset<buffer>::content_asset;
static std::shared_ptr<binary_asset> load(library& library, str_view address);
using content_asset<binary_asset, buffer>::content_asset;
static load_async_result load_async(library& library, str_view address);
};
class shader_asset final : public content_asset<shader_ptr> {
class shader_asset final : public content_asset<shader_asset, shader_ptr> {
public:
using content_asset<shader_ptr>::content_asset;
static std::shared_ptr<shader_asset> load(library& library, str_view address);
using content_asset<shader_asset, shader_ptr>::content_asset;
static load_async_result load_async(library& library, str_view address);
};
class texture_asset final : public content_asset<texture_ptr> {
class texture_asset final : public content_asset<texture_asset, texture_ptr> {
public:
using content_asset<texture_ptr>::content_asset;
static std::shared_ptr<texture_asset> load(library& library, str_view address);
using content_asset<texture_asset, texture_ptr>::content_asset;
static load_async_result load_async(library& library, str_view address);
};
class material_asset final : public content_asset<render::material> {
class material_asset final : public content_asset<material_asset, render::material> {
public:
using content_asset<render::material>::content_asset;
static std::shared_ptr<material_asset> load(library& library, str_view address);
using content_asset<material_asset, render::material>::content_asset;
static load_async_result load_async(library& library, str_view address);
};
}

View File

@@ -43,9 +43,13 @@ namespace e2d
library(const url& root);
~library() noexcept final;
const url& root() const noexcept;
template < typename T >
std::shared_ptr<T> load_asset(str_view address);
const url& root() const noexcept;
template < typename T >
stdex::promise<std::shared_ptr<T>> load_asset_async(str_view address);
private:
url root_;
};
@@ -63,8 +67,8 @@ namespace e2d
std::shared_ptr<T> find(str_hash address) const;
void store(str_hash address, const std::shared_ptr<T>& asset);
void clear();
void unload_unused_assets();
void clear() noexcept;
void unload_unused_assets() noexcept;
std::size_t asset_count() const noexcept;
private:
library& library_;

View File

@@ -19,24 +19,41 @@ namespace e2d
template < typename T >
std::shared_ptr<T> library::load_asset(str_view address) {
auto p = load_asset_async<T>(address);
if ( modules::is_initialized<deferrer>() && the<deferrer>().is_in_main_thread() ) {
const auto zero_us = time::to_chrono(make_microseconds(0));
while ( p.wait_for(zero_us) == stdex::promise_wait_status::timeout ) {
if ( 0 == the<deferrer>().scheduler().process_one_task().second ) {
std::this_thread::yield();
}
}
}
return p.get_or_default(nullptr);
}
template < typename T >
stdex::promise<std::shared_ptr<T>> library::load_asset_async(str_view address) {
if ( !modules::is_initialized<asset_cache<T>>() ) {
return T::load(*this, address);
return T::load_async(*this, address);
}
auto& cache = the<asset_cache<T>>();
const auto cached_asset = cache.find(address);
auto cached_asset = cache.find(address);
if ( cached_asset ) {
return cached_asset;
return stdex::make_resolved_promise(std::move(cached_asset));
}
const auto new_asset = T::load(*this, address);
if ( new_asset ) {
cache.store(address, new_asset);
return new_asset;
}
return nullptr;
return T::load_async(*this, address)
.then([
&cache,
address_hash = make_hash(address)
](const std::shared_ptr<T>& new_asset){
cache.store(address_hash, new_asset);
return new_asset;
});
}
//
@@ -66,16 +83,16 @@ namespace e2d
}
template < typename T >
void asset_cache<T>::clear() {
void asset_cache<T>::clear() noexcept {
std::lock_guard<std::mutex> guard(mutex_);
assets_.clear();
}
template < typename T >
void asset_cache<T>::unload_unused_assets() {
void asset_cache<T>::unload_unused_assets() noexcept {
std::lock_guard<std::mutex> guard(mutex_);
for ( auto iter = assets_.begin(); iter != assets_.end(); ) {
if ( iter->second.unique() ) {
if ( 1 == iter->second.use_count() ) {
iter = assets_.erase(iter);
} else {
++iter;

View File

@@ -0,0 +1,29 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#pragma once
#include <enduro2d/high/assets.hpp>
#include <3rdparty/pugixml/pugixml.hpp>
#include <3rdparty/rapidjson/schema.h>
#include <3rdparty/rapidjson/document.h>
namespace e2d
{
class xml_asset final : public content_asset<xml_asset, pugi::xml_document> {
public:
using content_asset<xml_asset, pugi::xml_document>::content_asset;
static load_async_result load_async(library& library, str_view address);
};
class json_asset final : public content_asset<json_asset, rapidjson::Document> {
public:
using content_asset<json_asset, rapidjson::Document>::content_asset;
static load_async_result load_async(library& library, str_view address);
};
}

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "assets.hpp"
namespace
{
using namespace e2d;
class binary_asset_loading_exception final : public asset_loading_exception {
const char* what() const noexcept final {
return "binary asset loading exception";
}
};
}
namespace e2d
{
binary_asset::load_async_result binary_asset::load_async(
library& library, str_view address)
{
E2D_UNUSED(library);
if ( !modules::is_initialized<vfs>() ) {
return stdex::make_rejected_promise<load_result>(
binary_asset_loading_exception());
}
const auto asset_url = library.root() / address;
return the<vfs>().load_async(asset_url)
.then([](auto&& content){
return std::make_shared<binary_asset>(
std::forward<decltype(content)>(content));
});
}
}

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "assets.hpp"
namespace
{
using namespace e2d;
class image_asset_loading_exception final : public asset_loading_exception {
const char* what() const noexcept final {
return "image asset loading exception";
}
};
}
namespace e2d
{
image_asset::load_async_result image_asset::load_async(
library& library, str_view address)
{
return library.load_asset_async<binary_asset>(address)
.then([](const binary_asset::ptr& image_data){
if ( !modules::is_initialized<deferrer>() ) {
throw image_asset_loading_exception();
}
return the<deferrer>().do_in_worker_thread([image_data](){
image content;
if ( !images::try_load_image(content, image_data->content()) ) {
throw image_asset_loading_exception();
}
return std::make_shared<image_asset>(std::move(content));
});
});
}
}

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "assets.hpp"
namespace
{
using namespace e2d;
class json_asset_loading_exception final : public asset_loading_exception {
const char* what() const noexcept final {
return "json asset loading exception";
}
};
}
namespace e2d
{
json_asset::load_async_result json_asset::load_async(
library& library, str_view address)
{
return library.load_asset_async<text_asset>(address)
.then([](const text_asset::ptr& json_data){
if ( !modules::is_initialized<deferrer>() ) {
throw json_asset_loading_exception();
}
return the<deferrer>().do_in_worker_thread([json_data](){
rapidjson::Document doc;
if ( doc.Parse(json_data->content().c_str()).HasParseError() ) {
throw json_asset_loading_exception();
}
return std::make_shared<json_asset>(std::move(doc));
});
});
}
}

View File

@@ -4,40 +4,20 @@
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include <enduro2d/high/assets.hpp>
#include <3rdparty/rapidjson/schema.h>
#include <3rdparty/rapidjson/document.h>
#include "assets.hpp"
namespace
{
using namespace e2d;
template < typename T >
const char* asset_schema_source() noexcept;
class material_asset_loading_exception final : public asset_loading_exception {
const char* what() const noexcept final {
return "material asset loading exception";
}
};
template <>
const char* asset_schema_source<shader_asset>() noexcept {
return R"json({
"type" : "object",
"required" : [ "vertex", "fragment" ],
"additionalProperties" : false,
"properties" : {
"vertex" : { "$ref": "#/definitions/generic_address" },
"fragment" : { "$ref": "#/definitions/generic_address" }
},
"definitions" : {
"generic_address" : {
"type" : "string",
"minLength" : 1
}
}
})json";
}
template <>
const char* asset_schema_source<material_asset>() noexcept {
return R"json({
const char* material_asset_schema_source = R"json(
{
"type" : "object",
"additionalProperties" : false,
"properties" : {
@@ -387,47 +367,24 @@ namespace
}
}
})json";
}
template < typename T >
const rapidjson::SchemaDocument& asset_file_schema() {
const rapidjson::SchemaDocument& material_asset_schema() {
static std::mutex mutex;
static std::unique_ptr<rapidjson::SchemaDocument> schema;
std::lock_guard<std::mutex> guard(mutex);
if ( !schema ) {
rapidjson::Document doc;
if ( doc.Parse(asset_schema_source<T>()).HasParseError() ) {
the<debug>().error("ASSETS: Failed to parse asset file schema");
throw bad_library_operation();
if ( doc.Parse(material_asset_schema_source).HasParseError() ) {
the<debug>().error("ASSETS: Failed to parse material asset schema");
throw material_asset_loading_exception();
}
schema = std::make_unique<rapidjson::SchemaDocument>(doc);
}
return *schema;
}
template < typename T >
bool load_json_asset_file(library& library, str_view address, rapidjson::Document& doc) {
const auto json_data = library.load_asset<text_asset>(address);
if ( !json_data ) {
return false;
}
if ( doc.Parse(json_data->content().c_str()).HasParseError() ) {
the<debug>().error("ASSETS: Failed to parse json asset file:\n"
"--> Address: %0",
address);
return false;
}
rapidjson::SchemaValidator validator(asset_file_schema<T>());
if ( !doc.Accept(validator) ) {
the<debug>().error("ASSETS: Failed to validate json asset file:\n"
"--> Address: %0",
address);
return false;
}
return true;
}
bool parse_stencil_op(str_view str, render::stencil_op& op) noexcept {
#define DEFINE_IF(x) if ( str == #x ) { op = render::stencil_op::x; return true; }
DEFINE_IF(keep);
@@ -741,222 +698,266 @@ namespace
return false;
}
bool parse_property_block_samplers(
stdex::promise<shader_ptr> parse_shader_block(
library& library,
str_view address,
const rapidjson::Value& root,
render::property_block& props)
str_view parent_address,
const rapidjson::Value& root)
{
const auto parent_address = path::parent_path(address);
E2D_ASSERT(root.IsString());
const auto shader_address =
path::combine(parent_address, root.GetString());
return library.load_asset_async<shader_asset>(shader_address)
.then([](const shader_asset::ptr& shader){
return shader->content();
});
}
if ( root.HasMember("samplers") ) {
E2D_ASSERT(root["samplers"].IsArray());
const auto& samplers_json = root["samplers"];
stdex::promise<texture_ptr> parse_texture_block(
library& library,
str_view parent_address,
const rapidjson::Value& root)
{
E2D_ASSERT(root.IsString());
const auto texture_address =
path::combine(parent_address, root.GetString());
return library.load_asset_async<texture_asset>(texture_address)
.then([](const texture_asset::ptr& texture){
return texture->content();
});
}
for ( rapidjson::SizeType i = 0; i < samplers_json.Size(); ++i ) {
E2D_ASSERT(samplers_json[i].IsObject());
const auto& sampler_json = samplers_json[i];
stdex::promise<std::pair<str_hash,render::sampler_state>> parse_sampler_state(
library& library,
str_view parent_address,
const rapidjson::Value& root)
{
render::sampler_state content;
E2D_ASSERT(sampler_json.HasMember("name") && sampler_json["name"].IsString());
E2D_ASSERT(sampler_json.HasMember("texture") && sampler_json["texture"].IsString());
E2D_ASSERT(root.HasMember("name") && root["name"].IsString());
E2D_ASSERT(root.HasMember("texture") && root["texture"].IsString());
const auto texture = library.load_asset<texture_asset>(
path::combine(parent_address, sampler_json["texture"].GetString()));
auto name_hash = make_hash(root["name"].GetString());
auto sampler = render::sampler_state()
.texture(texture ? texture->content() : texture_ptr());
auto texture_p = root.HasMember("texture")
? parse_texture_block(library, parent_address, root["texture"])
: stdex::make_resolved_promise<texture_ptr>(nullptr);
if ( sampler_json.HasMember("wrap") ) {
if ( sampler_json["wrap"].IsObject() ) {
const auto& sampler_wrap_json = sampler_json["wrap"];
if ( sampler_wrap_json.HasMember("s") ) {
E2D_ASSERT(sampler_wrap_json["s"].IsString());
auto wrap = sampler.s_wrap();
if ( parse_sampler_wrap(sampler_wrap_json["s"].GetString(), wrap) ) {
sampler.s_wrap(wrap);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler wrap");
}
}
if ( sampler_wrap_json.HasMember("t") ) {
E2D_ASSERT(sampler_wrap_json["t"].IsString());
auto wrap = sampler.t_wrap();
if ( parse_sampler_wrap(sampler_wrap_json["t"].GetString(), wrap) ) {
sampler.t_wrap(wrap);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler wrap");
}
}
if ( sampler_wrap_json.HasMember("r") ) {
E2D_ASSERT(sampler_wrap_json["r"].IsString());
auto wrap = sampler.r_wrap();
if ( parse_sampler_wrap(sampler_wrap_json["r"].GetString(), wrap) ) {
sampler.r_wrap(wrap);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler wrap");
}
}
} else if ( sampler_json["wrap"].IsString() ) {
auto wrap = sampler.s_wrap();
if ( parse_sampler_wrap(sampler_json["wrap"].GetString(), wrap) ) {
sampler.wrap(wrap);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler wrap");
}
if ( root.HasMember("wrap") ) {
const auto& wrap_json = root["wrap"];
if ( wrap_json.IsObject() ) {
if ( wrap_json.HasMember("s") ) {
E2D_ASSERT(wrap_json["s"].IsString());
auto wrap = content.s_wrap();
if ( parse_sampler_wrap(wrap_json["s"].GetString(), wrap) ) {
content.s_wrap(wrap);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler wrap");
}
}
if ( sampler_json.HasMember("filter") ) {
if ( sampler_json["filter"].IsObject() ) {
const auto& sampler_filter_json = sampler_json["filter"];
if ( sampler_filter_json.HasMember("min") ) {
E2D_ASSERT(sampler_filter_json["min"].IsString());
auto filter = sampler.min_filter();
if ( parse_sampler_min_filter(sampler_filter_json["min"].GetString(), filter) ) {
sampler.min_filter(filter);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler min filter");
}
}
if ( sampler_filter_json.HasMember("mag") ) {
E2D_ASSERT(sampler_filter_json["mag"].IsString());
auto filter = sampler.mag_filter();
if ( parse_sampler_mag_filter(sampler_filter_json["mag"].GetString(), filter) ) {
sampler.mag_filter(filter);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler mag filter");
}
}
} else if ( sampler_json["filter"].IsString() ) {
auto min_filter = sampler.min_filter();
if ( parse_sampler_min_filter(sampler_json["filter"].GetString(), min_filter) ) {
sampler.min_filter(min_filter);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler filter");
}
auto mag_filter = sampler.mag_filter();
if ( parse_sampler_mag_filter(sampler_json["filter"].GetString(), mag_filter) ) {
sampler.mag_filter(mag_filter);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler filter");
}
if ( wrap_json.HasMember("t") ) {
E2D_ASSERT(wrap_json["t"].IsString());
auto wrap = content.t_wrap();
if ( parse_sampler_wrap(wrap_json["t"].GetString(), wrap) ) {
content.t_wrap(wrap);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler wrap");
}
}
props.sampler(
sampler_json["name"].GetString(),
sampler);
if ( wrap_json.HasMember("r") ) {
E2D_ASSERT(wrap_json["r"].IsString());
auto wrap = content.r_wrap();
if ( parse_sampler_wrap(wrap_json["r"].GetString(), wrap) ) {
content.r_wrap(wrap);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler wrap");
}
}
} else if ( wrap_json.IsString() ) {
auto wrap = content.s_wrap();
if ( parse_sampler_wrap(wrap_json.GetString(), wrap) ) {
content.wrap(wrap);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler wrap");
}
}
}
return true;
if ( root.HasMember("filter") ) {
const auto& filter_json = root["filter"];
if ( filter_json.IsObject() ) {
if ( filter_json.HasMember("min") ) {
E2D_ASSERT(filter_json["min"].IsString());
auto filter = content.min_filter();
if ( parse_sampler_min_filter(filter_json["min"].GetString(), filter) ) {
content.min_filter(filter);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler min filter");
}
}
if ( filter_json.HasMember("mag") ) {
E2D_ASSERT(filter_json["mag"].IsString());
auto filter = content.mag_filter();
if ( parse_sampler_mag_filter(filter_json["mag"].GetString(), filter) ) {
content.mag_filter(filter);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler mag filter");
}
}
} else if ( filter_json.IsString() ) {
auto min_filter = content.min_filter();
if ( parse_sampler_min_filter(filter_json.GetString(), min_filter) ) {
content.min_filter(min_filter);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler filter");
}
auto mag_filter = content.mag_filter();
if ( parse_sampler_mag_filter(filter_json.GetString(), mag_filter) ) {
content.mag_filter(mag_filter);
} else {
E2D_ASSERT_MSG(false, "unexpected sampler filter");
}
}
}
return texture_p
.then([name_hash, content](const texture_ptr& texture) mutable {
content.texture(texture);
return std::make_pair(name_hash, content);
});
}
bool parse_property_block_properties(
const rapidjson::Value& root,
render::property_block& props)
{
if ( root.HasMember("properties") ) {
E2D_ASSERT(root["properties"].IsArray());
const auto& root_properties = root["properties"];
E2D_ASSERT(root.IsArray());
for ( rapidjson::SizeType i = 0; i < root.Size(); ++i ) {
E2D_ASSERT(root[i].IsObject());
const auto& property_json = root[i];
for ( rapidjson::SizeType i = 0; i < root_properties.Size(); ++i ) {
E2D_ASSERT(root_properties[i].IsObject());
const auto& property_json = root_properties[i];
E2D_ASSERT(property_json.HasMember("name") && property_json["name"].IsString());
E2D_ASSERT(property_json.HasMember("type") && property_json["type"].IsString());
E2D_ASSERT(property_json.HasMember("name") && property_json["name"].IsString());
E2D_ASSERT(property_json.HasMember("type") && property_json["type"].IsString());
if ( 0 == std::strcmp(property_json["type"].GetString(), "i32") ) {
i32 value = 0;
if ( property_json.HasMember("value") ) {
E2D_ASSERT(property_json["value"].IsNumber());
value = property_json["value"].GetInt();
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "f32") ) {
f32 value = 0;
if ( property_json.HasMember("value") ) {
E2D_ASSERT(property_json["value"].IsNumber());
value = property_json["value"].GetFloat();
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v2i") ) {
v2i value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v3i") ) {
v3i value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v4i") ) {
v4i value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v2f") ) {
v2f value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v3f") ) {
v3f value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v4f") ) {
v4f value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else {
E2D_ASSERT_MSG(false, "unexpected property type");
return false;
if ( 0 == std::strcmp(property_json["type"].GetString(), "i32") ) {
i32 value = 0;
if ( property_json.HasMember("value") ) {
E2D_ASSERT(property_json["value"].IsNumber());
value = property_json["value"].GetInt();
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "f32") ) {
f32 value = 0;
if ( property_json.HasMember("value") ) {
E2D_ASSERT(property_json["value"].IsNumber());
value = property_json["value"].GetFloat();
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v2i") ) {
v2i value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v3i") ) {
v3i value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v4i") ) {
v4i value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v2f") ) {
v2f value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v3f") ) {
v3f value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else if ( 0 == std::strcmp(property_json["type"].GetString(), "v4f") ) {
v4f value;
if ( property_json.HasMember("value") ) {
if ( !parse_property_value(property_json["value"], value) ) {
E2D_ASSERT_MSG(false, "unexpected property value");
return false;
}
}
props.property(property_json["name"].GetString(), value);
} else {
E2D_ASSERT_MSG(false, "unexpected property type");
return false;
}
}
return true;
}
bool parse_property_block(
stdex::promise<render::property_block> parse_property_block(
library& library,
str_view address,
const rapidjson::Value& root,
render::property_block& props)
str_view parent_address,
const rapidjson::Value& root)
{
return parse_property_block_samplers(library, address, root, props)
&& parse_property_block_properties(root, props);
render::property_block content;
vector<stdex::promise<std::pair<str_hash, render::sampler_state>>> samplers_p;
if ( root.HasMember("samplers") ) {
E2D_ASSERT(root["samplers"].IsArray());
const auto& samplers_json = root["samplers"];
samplers_p.reserve(samplers_json.Size());
for ( rapidjson::SizeType i = 0; i < samplers_json.Size(); ++i ) {
E2D_ASSERT(samplers_json[i].IsObject());
const auto& sampler_json = samplers_json[i];
samplers_p.emplace_back(
parse_sampler_state(library, parent_address, sampler_json));
}
}
if ( root.HasMember("properties") ) {
E2D_ASSERT(root["properties"].IsArray());
const auto& properties_json = root["properties"];
if ( !parse_property_block_properties(properties_json, content) ) {
return stdex::make_rejected_promise<render::property_block>(
material_asset_loading_exception());
}
}
return stdex::make_all_promise(samplers_p)
.then([content](auto&& results) mutable {
for ( auto& result : results ) {
content.sampler(
std::move(result.first),
std::move(result.second));
}
return content;
});
}
bool parse_depth_state(
@@ -1292,306 +1293,156 @@ namespace
return true;
}
bool parse_state_block(
const rapidjson::Value& root,
render::state_block& states)
stdex::promise<render::state_block> parse_state_block(
const rapidjson::Value& root)
{
render::state_block content;
if ( root.HasMember("depth_state") ) {
E2D_ASSERT(root["depth_state"].IsObject());
if ( !parse_depth_state(root["depth_state"], states.depth()) ) {
return false;
if ( !parse_depth_state(root["depth_state"], content.depth()) ) {
return stdex::make_rejected_promise<render::state_block>(
material_asset_loading_exception());
}
}
if ( root.HasMember("stencil_state") ) {
E2D_ASSERT(root["stencil_state"].IsObject());
if ( !parse_stencil_state(root["stencil_state"], states.stencil()) ) {
return false;
if ( !parse_stencil_state(root["stencil_state"], content.stencil()) ) {
return stdex::make_rejected_promise<render::state_block>(
material_asset_loading_exception());
}
}
if ( root.HasMember("culling_state") ) {
E2D_ASSERT(root["culling_state"].IsObject());
if ( !parse_culling_state(root["culling_state"], states.culling()) ) {
return false;
if ( !parse_culling_state(root["culling_state"], content.culling()) ) {
return stdex::make_rejected_promise<render::state_block>(
material_asset_loading_exception());
}
}
if ( root.HasMember("blending_state") ) {
E2D_ASSERT(root["blending_state"].IsObject());
if ( !parse_blending_state(root["blending_state"], states.blending()) ) {
return false;
if ( !parse_blending_state(root["blending_state"], content.blending()) ) {
return stdex::make_rejected_promise<render::state_block>(
material_asset_loading_exception());
}
}
if ( root.HasMember("capabilities_state") ) {
E2D_ASSERT(root["capabilities_state"].IsObject());
if ( !parse_capabilities_state(root["capabilities_state"], states.capabilities()) ) {
return false;
if ( !parse_capabilities_state(root["capabilities_state"], content.capabilities()) ) {
return stdex::make_rejected_promise<render::state_block>(
material_asset_loading_exception());
}
}
return true;
return stdex::make_resolved_promise(content);
}
bool parse_pass_state(
stdex::promise<render::pass_state> parse_pass_state(
library& library,
str_view address,
const rapidjson::Value& root,
render::pass_state& pass)
str_view parent_address,
const rapidjson::Value& root)
{
const auto parent_address = path::parent_path(address);
auto shader_p = root.HasMember("shader")
? parse_shader_block(library, parent_address, root["shader"])
: stdex::make_resolved_promise<shader_ptr>(nullptr);
if ( root.HasMember("shader") ) {
E2D_ASSERT(root["shader"].IsString());
auto state_block_p = root.HasMember("state_block")
? parse_state_block(root["state_block"])
: stdex::make_resolved_promise<render::state_block>(render::state_block());
const auto shader = library.load_asset<shader_asset>(
path::combine(parent_address, root["shader"].GetString()));
auto property_block_p = root.HasMember("property_block")
? parse_property_block(library, parent_address, root["property_block"])
: stdex::make_resolved_promise<render::property_block>(render::property_block());
pass.shader(shader ? shader->content() : shader_ptr());
}
return stdex::make_tuple_promise(std::make_tuple(
std::move(shader_p),
std::move(state_block_p),
std::move(property_block_p)
)).then([](const std::tuple<
shader_ptr,
render::state_block,
render::property_block
>& result) {
render::pass_state content;
content.shader(std::get<0>(result));
content.states(std::get<1>(result));
content.properties(std::get<2>(result));
return content;
});
}
if ( root.HasMember("state_block") ) {
E2D_ASSERT(root["state_block"].IsObject());
const auto& state_block_json = root["state_block"];
stdex::promise<render::material> parse_material(
library& library,
str_view parent_address,
const rapidjson::Value& root)
{
vector<stdex::promise<render::pass_state>> passes_p;
render::state_block states;
if ( !parse_state_block(state_block_json, states) ) {
return false;
if ( root.HasMember("passes") ) {
E2D_ASSERT(root["passes"].IsArray());
const auto& passes_json = root["passes"];
passes_p.reserve(passes_json.Size());
for ( rapidjson::SizeType i = 0; i < passes_json.Size(); ++i ) {
E2D_ASSERT(passes_json[i].IsObject());
const auto& pass_json = passes_json[i];
passes_p.emplace_back(
parse_pass_state(library, parent_address, pass_json));
}
pass.states(states);
}
if ( root.HasMember("property_block") ) {
E2D_ASSERT(root["property_block"].IsObject());
const auto& property_block_json = root["property_block"];
auto property_block_p = root.HasMember("property_block")
? parse_property_block(library, parent_address, root["property_block"])
: stdex::make_resolved_promise(render::property_block());
render::property_block props;
if ( !parse_property_block(library, address, property_block_json, props) ) {
return false;
return stdex::make_tuple_promise(std::make_tuple(
stdex::make_all_promise(passes_p),
std::move(property_block_p)))
.then([](const std::tuple<
vector<render::pass_state>,
render::property_block
>& results) {
render::material content;
for ( auto& pass : std::get<0>(results) ) {
content.add_pass(pass);
}
pass.properties(props);
}
return true;
content.properties(std::get<1>(results));
return content;
});
}
}
namespace e2d
{
//
// text_asset
//
std::shared_ptr<text_asset> text_asset::load(library& library, str_view address) {
E2D_UNUSED(library);
const auto asset_url = library.root() / address;
input_stream_uptr stream = modules::is_initialized<vfs>()
? the<vfs>().read(asset_url)
: input_stream_uptr();
if ( !stream ) {
the<debug>().error("ASSETS: Failed to open text asset file:\n"
"--> Url: %0",
asset_url);
return nullptr;
}
str content;
if ( !streams::try_read_tail(content, stream) ) {
the<debug>().error("ASSETS: Failed to read text asset file:\n"
"--> Url: %0",
asset_url);
return nullptr;
}
return std::make_shared<text_asset>(std::move(content));
}
//
// mesh_asset
//
std::shared_ptr<mesh_asset> mesh_asset::load(library& library, str_view address) {
const auto mesh_data = library.load_asset<binary_asset>(address);
if ( !mesh_data ) {
return nullptr;
}
mesh content;
if ( !meshes::try_load_mesh(content, mesh_data->content()) ) {
the<debug>().error("ASSETS: Failed to create mesh asset:\n"
"--> Address: %0",
address);
return nullptr;
}
return std::make_shared<mesh_asset>(std::move(content));
}
//
// image_asset
//
std::shared_ptr<image_asset> image_asset::load(library& library, str_view address) {
const auto image_data = library.load_asset<binary_asset>(address);
if ( !image_data ) {
return nullptr;
}
image content;
if ( !images::try_load_image(content, image_data->content()) ) {
the<debug>().error("ASSETS: Failed to create image asset:\n"
"--> Address: %0",
address);
return nullptr;
}
return std::make_shared<image_asset>(std::move(content));
}
//
// binary_asset
//
std::shared_ptr<binary_asset> binary_asset::load(library& library, str_view address) {
E2D_UNUSED(library);
const auto asset_url = library.root() / address;
input_stream_uptr stream = modules::is_initialized<vfs>()
? the<vfs>().read(asset_url)
: input_stream_uptr();
if ( !stream ) {
the<debug>().error("ASSETS: Failed to open binary asset file:\n"
"--> Url: %0",
asset_url);
return nullptr;
}
buffer content;
if ( !streams::try_read_tail(content, stream) ) {
the<debug>().error("ASSETS: Failed to read binary asset file:\n"
"--> Url: %0",
asset_url);
return nullptr;
}
return std::make_shared<binary_asset>(std::move(content));
}
//
// shader_asset
//
std::shared_ptr<shader_asset> shader_asset::load(library& library, str_view address) {
rapidjson::Document doc;
if ( !load_json_asset_file<shader_asset>(library, address, doc) ) {
return nullptr;
}
const auto parent_address = path::parent_path(address);
E2D_ASSERT(doc.HasMember("vertex") && doc["vertex"].IsString());
const auto vertex_source_data = library.load_asset<text_asset>(
path::combine(parent_address, doc["vertex"].GetString()));
if ( !vertex_source_data ) {
return nullptr;
}
E2D_ASSERT(doc.HasMember("fragment") && doc["fragment"].IsString());
const auto fragment_source_data = library.load_asset<text_asset>(
path::combine(parent_address, doc["fragment"].GetString()));
if ( !fragment_source_data ) {
return nullptr;
}
const auto content = modules::is_initialized<render>()
? the<render>().create_shader(
vertex_source_data->content(),
fragment_source_data->content())
: shader_ptr();
if ( !content ) {
the<debug>().error("ASSETS: Failed to create shader asset:\n"
"--> Address: %0",
address);
return nullptr;
}
return std::make_shared<shader_asset>(content);
}
//
// texture_asset
//
std::shared_ptr<texture_asset> texture_asset::load(library& library, str_view address) {
const auto texture_data = library.load_asset<image_asset>(address);
if ( !texture_data ) {
return nullptr;
}
const auto content = modules::is_initialized<render>()
? the<render>().create_texture(texture_data->content())
: texture_ptr();
if ( !content ) {
the<debug>().error("ASSETS: Failed to create texture asset:\n"
"--> Address: %0",
address);
return nullptr;
}
return std::make_shared<texture_asset>(content);
}
//
// material_asset
//
std::shared_ptr<material_asset> material_asset::load(library& library, str_view address) {
rapidjson::Document doc;
if ( !load_json_asset_file<material_asset>(library, address, doc) ) {
return nullptr;
}
render::material content;
if ( doc.HasMember("passes") ) {
E2D_ASSERT(doc["passes"].IsArray());
const auto& passes_json = doc["passes"];
for ( rapidjson::SizeType i = 0; i < passes_json.Size(); ++i ) {
E2D_ASSERT(passes_json[i].IsObject());
const auto& pass_json = passes_json[i];
render::pass_state pass;
if ( !parse_pass_state(library, address, pass_json, pass) ) {
return nullptr;
material_asset::load_async_result material_asset::load_async(
library& library, str_view address)
{
return library.load_asset_async<json_asset>(address)
.then([
&library,
parent_address = path::parent_path(address)
](const json_asset::ptr& material_data){
if ( !modules::is_initialized<deferrer>() ) {
throw material_asset_loading_exception();
}
content.add_pass(pass);
}
}
if ( doc.HasMember("property_block") ) {
E2D_ASSERT(doc["property_block"].IsObject());
const auto& property_block_json = doc["property_block"];
render::property_block props;
if ( !parse_property_block(library, address, property_block_json, props) ) {
return nullptr;
}
content.properties(props);
}
return std::make_shared<material_asset>(content);
return the<deferrer>().do_in_worker_thread([material_data](){
const rapidjson::Document& doc = material_data->content();
rapidjson::SchemaValidator validator(material_asset_schema());
if ( !doc.Accept(validator) ) {
throw material_asset_loading_exception();
}
})
.then([&library, parent_address, material_data](){
return parse_material(
library, parent_address, material_data->content());
})
.then([](const render::material& material){
return std::make_shared<material_asset>(material);
});
});
}
}

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "assets.hpp"
namespace
{
using namespace e2d;
class mesh_asset_loading_exception final : public asset_loading_exception {
const char* what() const noexcept final {
return "mesh asset loading exception";
}
};
}
namespace e2d
{
mesh_asset::load_async_result mesh_asset::load_async(
library& library, str_view address)
{
return library.load_asset_async<binary_asset>(address)
.then([](const binary_asset::ptr& mesh_data){
if ( !modules::is_initialized<deferrer>() ) {
throw mesh_asset_loading_exception();
}
return the<deferrer>().do_in_worker_thread([mesh_data](){
mesh content;
if ( !meshes::try_load_mesh(content, mesh_data->content()) ) {
throw mesh_asset_loading_exception();
}
return std::make_shared<mesh_asset>(std::move(content));
});
});
}
}

View File

@@ -0,0 +1,124 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "assets.hpp"
namespace
{
using namespace e2d;
class shader_asset_loading_exception final : public asset_loading_exception {
const char* what() const noexcept final {
return "shader asset loading exception";
}
};
const char* shader_asset_schema_source = R"json(
{
"type" : "object",
"required" : [ "vertex", "fragment" ],
"additionalProperties" : false,
"properties" : {
"vertex" : { "$ref": "#/definitions/generic_address" },
"fragment" : { "$ref": "#/definitions/generic_address" }
},
"definitions" : {
"generic_address" : {
"type" : "string",
"minLength" : 1
}
}
})json";
const rapidjson::SchemaDocument& shader_asset_schema() {
static std::mutex mutex;
static std::unique_ptr<rapidjson::SchemaDocument> schema;
std::lock_guard<std::mutex> guard(mutex);
if ( !schema ) {
rapidjson::Document doc;
if ( doc.Parse(shader_asset_schema_source).HasParseError() ) {
the<debug>().error("ASSETS: Failed to parse shader asset schema");
throw shader_asset_loading_exception();
}
schema = std::make_unique<rapidjson::SchemaDocument>(doc);
}
return *schema;
}
stdex::promise<shader_ptr> parse_shader(
library& library,
str_view parent_address,
const rapidjson::Value& root)
{
E2D_ASSERT(root.HasMember("vertex") && root["vertex"].IsString());
auto vertex_p = library.load_asset_async<text_asset>(
path::combine(parent_address, root["vertex"].GetString()));
E2D_ASSERT(root.HasMember("fragment") && root["fragment"].IsString());
auto fragment_p = library.load_asset_async<text_asset>(
path::combine(parent_address, root["fragment"].GetString()));
return stdex::make_tuple_promise(std::make_tuple(
std::move(vertex_p),
std::move(fragment_p)))
.then([](const std::tuple<
text_asset::ptr,
text_asset::ptr
>& results){
if ( !modules::is_initialized<deferrer>() ) {
throw shader_asset_loading_exception();
}
return the<deferrer>().do_in_main_thread([results](){
if ( !modules::is_initialized<render>() ) {
throw shader_asset_loading_exception();
}
const shader_ptr content = the<render>().create_shader(
std::get<0>(results)->content(),
std::get<1>(results)->content());
if ( !content ) {
throw shader_asset_loading_exception();
}
return content;
});
});
}
}
namespace e2d
{
shader_asset::load_async_result shader_asset::load_async(
library& library, str_view address)
{
return library.load_asset_async<json_asset>(address)
.then([
&library,
parent_address = path::parent_path(address)
](const json_asset::ptr& shader_data){
if ( !modules::is_initialized<deferrer>() ) {
throw shader_asset_loading_exception();
}
return the<deferrer>().do_in_worker_thread([shader_data](){
const rapidjson::Document& doc = shader_data->content();
rapidjson::SchemaValidator validator(shader_asset_schema());
if ( !doc.Accept(validator) ) {
throw shader_asset_loading_exception();
}
})
.then([&library, parent_address, shader_data](){
return parse_shader(
library, parent_address, shader_data->content());
})
.then([](const shader_ptr& shader){
if ( !shader ) {
throw shader_asset_loading_exception();
}
return std::make_shared<shader_asset>(shader);
});
});
}
}

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "assets.hpp"
namespace
{
using namespace e2d;
class text_asset_loading_exception final : public asset_loading_exception {
const char* what() const noexcept final {
return "text asset loading exception";
}
};
}
namespace e2d
{
text_asset::load_async_result text_asset::load_async(
library& library, str_view address)
{
E2D_UNUSED(library);
if ( !modules::is_initialized<vfs>() ) {
return stdex::make_rejected_promise<load_result>(
text_asset_loading_exception());
}
const auto asset_url = library.root() / address;
return the<vfs>().load_as_string_async(asset_url)
.then([](auto&& content){
return std::make_shared<text_asset>(
std::forward<decltype(content)>(content));
});
}
}

View File

@@ -0,0 +1,43 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "assets.hpp"
namespace
{
using namespace e2d;
class texture_asset_loading_exception final : public asset_loading_exception {
const char* what() const noexcept final {
return "texture asset loading exception";
}
};
}
namespace e2d
{
texture_asset::load_async_result texture_asset::load_async(
library& library, str_view address)
{
return library.load_asset_async<image_asset>(address)
.then([](const image_asset::ptr& texture_data){
if ( !modules::is_initialized<deferrer>() ) {
throw texture_asset_loading_exception();
}
return the<deferrer>().do_in_main_thread([texture_data](){
if ( !modules::is_initialized<render>() ) {
throw texture_asset_loading_exception();
}
const texture_ptr content = the<render>().create_texture(
texture_data->content());
if ( !content ) {
throw texture_asset_loading_exception();
}
return std::make_shared<texture_asset>(content);
});
});
}
}

View File

@@ -0,0 +1,39 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018 Matvey Cherevko
******************************************************************************/
#include "assets.hpp"
namespace
{
using namespace e2d;
class xml_asset_loading_exception final : public asset_loading_exception {
const char* what() const noexcept final {
return "xml asset loading exception";
}
};
}
namespace e2d
{
xml_asset::load_async_result xml_asset::load_async(
library& library, str_view address)
{
return library.load_asset_async<text_asset>(address)
.then([](const text_asset::ptr& xml_data){
if ( !modules::is_initialized<deferrer>() ) {
throw xml_asset_loading_exception();
}
return the<deferrer>().do_in_worker_thread([xml_data](){
pugi::xml_document doc;
if ( !doc.load_string(xml_data->content().c_str()) ) {
throw xml_asset_loading_exception();
}
return std::make_shared<xml_asset>(std::move(doc));
});
});
}
}

View File

@@ -84,9 +84,7 @@ namespace e2d
starter::starter(int argc, char *argv[], const parameters& params) {
safe_module_initialize<engine>(argc, argv, params.engine_params());
safe_module_initialize<library>(params.library_root());
safe_module_initialize<asset_cache<text_asset>>(the<library>());
safe_module_initialize<asset_cache<mesh_asset>>(the<library>());
safe_module_initialize<asset_cache<image_asset>>(the<library>());

View File

@@ -7,68 +7,55 @@
#include "_high.hpp"
using namespace e2d;
TEST_CASE("library"){
modules::initialize<vfs>();
modules::initialize<debug>();
//modules::initialize<window>(v2u{640,480}, "", false);
//modules::initialize<render>(the<debug>(), the<window>());
modules::initialize<library>(url{"resources://bin/library"});
modules::initialize<asset_cache<text_asset>>(the<library>());
modules::initialize<asset_cache<image_asset>>(the<library>());
modules::initialize<asset_cache<binary_asset>>(the<library>());
{
the<debug>().register_sink<debug_console_sink>();
the<vfs>().register_scheme<filesystem_file_source>("file");
}
{
str resources;
REQUIRE(filesystem::extract_predef_path(
resources,
filesystem::predef_path::resources));
REQUIRE(the<vfs>().register_scheme_alias(
"resources",
{"file", resources}));
}
{
library& l = the<library>();
namespace
{
class safe_starter_initializer final : private noncopyable {
public:
safe_starter_initializer() {
modules::initialize<starter>(0, nullptr,
starter::parameters(
engine::parameters("library_untests", "enduro2d")
.without_graphics(true)));
}
~safe_starter_initializer() noexcept {
modules::shutdown<starter>();
}
};
}
TEST_CASE("library"){
safe_starter_initializer initializer;
library& l = the<library>();
{
auto text_res = l.load_asset<text_asset>("text_asset.txt");
REQUIRE(text_res);
REQUIRE(text_res->content() == "hello");
auto text_res_from_cache = l.load_asset<text_asset>("text_asset.txt");
REQUIRE(text_res_from_cache);
REQUIRE(text_res_from_cache.get() == text_res.get());
the<asset_cache<text_asset>>().unload_unused_assets();
REQUIRE(the<asset_cache<text_asset>>().asset_count() == 1);
text_res.reset();
text_res_from_cache.reset();
the<deferrer>().worker().wait_all();
the<asset_cache<text_asset>>().unload_unused_assets();
REQUIRE(the<asset_cache<text_asset>>().asset_count() == 0);
}
{
auto binary_res = l.load_asset<binary_asset>("binary_asset.bin");
REQUIRE(binary_res);
REQUIRE(binary_res->content() == buffer("world", 5));
auto empty_res = l.load_asset<binary_asset>("empty_asset");
REQUIRE_FALSE(empty_res);
auto binary_res_from_cache = l.load_asset<binary_asset>("binary_asset.bin");
REQUIRE(binary_res == binary_res_from_cache);
REQUIRE(the<asset_cache<text_asset>>().asset_count() == 1);
REQUIRE(the<asset_cache<binary_asset>>().asset_count() == 1);
the<asset_cache<text_asset>>().unload_unused_assets();
the<asset_cache<binary_asset>>().unload_unused_assets();
REQUIRE(the<asset_cache<text_asset>>().asset_count() == 1);
REQUIRE(the<asset_cache<binary_asset>>().asset_count() == 1);
text_res.reset();
the<asset_cache<text_asset>>().unload_unused_assets();
REQUIRE(the<asset_cache<text_asset>>().asset_count() == 0);
REQUIRE(the<asset_cache<binary_asset>>().asset_count() == 1);
binary_res.reset();
the<asset_cache<binary_asset>>().unload_unused_assets();
REQUIRE(the<asset_cache<binary_asset>>().asset_count() == 1);
binary_res_from_cache.reset();
the<asset_cache<binary_asset>>().unload_unused_assets();
REQUIRE(the<asset_cache<binary_asset>>().asset_count() == 0);
}
{
library& l = the<library>();
auto empty_res = l.load_asset<binary_asset>("empty_asset");
REQUIRE_FALSE(empty_res);
}
{
auto image_res = l.load_asset<image_asset>("image.png");
REQUIRE(image_res);
REQUIRE(!image_res->content().empty());
@@ -76,107 +63,112 @@ TEST_CASE("library"){
REQUIRE(the<asset_cache<image_asset>>().find("image.png"));
REQUIRE(the<asset_cache<binary_asset>>().find("image.png"));
the<deferrer>().worker().wait_all();
the<asset_cache<binary_asset>>().unload_unused_assets();
REQUIRE(the<asset_cache<image_asset>>().find("image.png"));
REQUIRE_FALSE(the<asset_cache<binary_asset>>().find("image.png"));
image_res.reset();
the<deferrer>().worker().wait_all();
the<asset_cache<image_asset>>().unload_unused_assets();
REQUIRE_FALSE(the<asset_cache<image_asset>>().find("image.png"));
REQUIRE_FALSE(the<asset_cache<binary_asset>>().find("image.png"));
}
{
//library& l = the<library>();
//auto shader_res = l.load_asset<shader_asset>("shader.json");
//REQUIRE(shader_res);
//REQUIRE(shader_res->content());
if ( modules::is_initialized<render>() ) {
auto shader_res = l.load_asset<shader_asset>("shader.json");
REQUIRE(shader_res);
REQUIRE(shader_res->content());
auto texture_res = l.load_asset<texture_asset>("image.png");
REQUIRE(texture_res);
REQUIRE(texture_res->content());
}
}
{
library& l = the<library>();
auto material_res = l.load_asset<material_asset>("material.json");
REQUIRE(material_res);
{
const auto* sampler = material_res->content().properties().sampler("s");
REQUIRE(sampler);
REQUIRE(sampler->s_wrap() == render::sampler_wrap::clamp);
REQUIRE(sampler->t_wrap() == render::sampler_wrap::repeat);
REQUIRE(sampler->r_wrap() == render::sampler_wrap::mirror);
REQUIRE(sampler->min_filter() == render::sampler_min_filter::linear_mipmap_linear);
REQUIRE(sampler->mag_filter() == render::sampler_mag_filter::linear);
if ( modules::is_initialized<render>() ) {
auto material_res = l.load_asset<material_asset>("material.json");
REQUIRE(material_res);
{
auto texture_res = l.load_asset<texture_asset>("image.png");
REQUIRE(texture_res);
REQUIRE(texture_res->content());
const auto* sampler = material_res->content().properties().sampler("s");
REQUIRE(sampler);
REQUIRE(sampler->s_wrap() == render::sampler_wrap::clamp);
REQUIRE(sampler->t_wrap() == render::sampler_wrap::repeat);
REQUIRE(sampler->r_wrap() == render::sampler_wrap::mirror);
REQUIRE(sampler->min_filter() == render::sampler_min_filter::linear_mipmap_linear);
REQUIRE(sampler->mag_filter() == render::sampler_mag_filter::linear);
REQUIRE(texture_res->content() == sampler->texture());
}
{
const auto* property = material_res->content().properties().property("i");
REQUIRE(property);
REQUIRE(property->index() == 0);
REQUIRE(stdex::get<i32>(*property) == 42);
}
REQUIRE(material_res->content().pass_count() == 1);
const auto& pass = material_res->content().pass(0);
{
const auto* property = pass.properties().property("f");
REQUIRE(property);
REQUIRE(property->index() == 1);
REQUIRE(math::approximately(stdex::get<f32>(*property), 4.2f));
}
{
const auto* property = pass.properties().property("v1");
REQUIRE(property);
REQUIRE(property->index() == 2);
REQUIRE(stdex::get<v2i>(*property) == v2i(1,2));
}
{
const auto* property = pass.properties().property("v2");
REQUIRE(property);
REQUIRE(property->index() == 6);
REQUIRE(stdex::get<v3f>(*property) == v3f(3.f));
}
{
const auto* property = pass.properties().property("v3");
REQUIRE(property);
REQUIRE(property->index() == 4);
REQUIRE(stdex::get<v4i>(*property) == v4i(1,2,3,4));
}
{
REQUIRE(pass.states().depth().range_near() == 1.f);
REQUIRE(pass.states().depth().range_far() == 2.f);
REQUIRE(pass.states().depth().write() == false);
REQUIRE(pass.states().depth().func() == render::compare_func::greater);
REQUIRE(pass.states().stencil().write() == 2u);
REQUIRE(pass.states().stencil().func() == render::compare_func::never);
REQUIRE(pass.states().stencil().ref() == 4u);
REQUIRE(pass.states().stencil().mask() == 5u);
REQUIRE(pass.states().stencil().pass() == render::stencil_op::incr);
REQUIRE(pass.states().stencil().sfail() == render::stencil_op::decr);
REQUIRE(pass.states().stencil().zfail() == render::stencil_op::invert);
REQUIRE(pass.states().culling().mode() == render::culling_mode::cw);
REQUIRE(pass.states().culling().face() == render::culling_face::front);
REQUIRE(pass.states().capabilities().culling());
REQUIRE(pass.states().capabilities().blending());
REQUIRE(pass.states().capabilities().depth_test());
REQUIRE(pass.states().capabilities().stencil_test());
REQUIRE(pass.states().blending().constant_color() == color::white());
REQUIRE(pass.states().blending().color_mask() == render::blending_color_mask::gba);
REQUIRE(pass.states().blending().src_rgb_factor() == render::blending_factor::dst_alpha);
REQUIRE(pass.states().blending().src_alpha_factor() == render::blending_factor::dst_alpha);
REQUIRE(pass.states().blending().dst_rgb_factor() == render::blending_factor::dst_color);
REQUIRE(pass.states().blending().dst_alpha_factor() == render::blending_factor::src_color);
REQUIRE(pass.states().blending().rgb_equation() == render::blending_equation::subtract);
REQUIRE(pass.states().blending().alpha_equation() == render::blending_equation::reverse_subtract);
}
}
{
const auto* property = material_res->content().properties().property("i");
REQUIRE(property);
REQUIRE(property->index() == 0);
REQUIRE(stdex::get<i32>(*property) == 42);
}
REQUIRE(material_res->content().pass_count() == 1);
const auto& pass = material_res->content().pass(0);
//REQUIRE(pass.shader());
{
const auto* property = pass.properties().property("f");
REQUIRE(property);
REQUIRE(property->index() == 1);
REQUIRE(math::approximately(stdex::get<f32>(*property), 4.2f));
}
{
const auto* property = pass.properties().property("v1");
REQUIRE(property);
REQUIRE(property->index() == 2);
REQUIRE(stdex::get<v2i>(*property) == v2i(1,2));
}
{
const auto* property = pass.properties().property("v2");
REQUIRE(property);
REQUIRE(property->index() == 6);
REQUIRE(stdex::get<v3f>(*property) == v3f(3.f));
}
{
const auto* property = pass.properties().property("v3");
REQUIRE(property);
REQUIRE(property->index() == 4);
REQUIRE(stdex::get<v4i>(*property) == v4i(1,2,3,4));
}
REQUIRE(pass.states().depth().range_near() == 1.f);
REQUIRE(pass.states().depth().range_far() == 2.f);
REQUIRE(pass.states().depth().write() == false);
REQUIRE(pass.states().depth().func() == render::compare_func::greater);
REQUIRE(pass.states().stencil().write() == 2u);
REQUIRE(pass.states().stencil().func() == render::compare_func::never);
REQUIRE(pass.states().stencil().ref() == 4u);
REQUIRE(pass.states().stencil().mask() == 5u);
REQUIRE(pass.states().stencil().pass() == render::stencil_op::incr);
REQUIRE(pass.states().stencil().sfail() == render::stencil_op::decr);
REQUIRE(pass.states().stencil().zfail() == render::stencil_op::invert);
REQUIRE(pass.states().culling().mode() == render::culling_mode::cw);
REQUIRE(pass.states().culling().face() == render::culling_face::front);
REQUIRE(pass.states().capabilities().culling());
REQUIRE(pass.states().capabilities().blending());
REQUIRE(pass.states().capabilities().depth_test());
REQUIRE(pass.states().capabilities().stencil_test());
REQUIRE(pass.states().blending().constant_color() == color::white());
REQUIRE(pass.states().blending().color_mask() == render::blending_color_mask::gba);
REQUIRE(pass.states().blending().src_rgb_factor() == render::blending_factor::dst_alpha);
REQUIRE(pass.states().blending().src_alpha_factor() == render::blending_factor::dst_alpha);
REQUIRE(pass.states().blending().dst_rgb_factor() == render::blending_factor::dst_color);
REQUIRE(pass.states().blending().dst_alpha_factor() == render::blending_factor::src_color);
REQUIRE(pass.states().blending().rgb_equation() == render::blending_equation::subtract);
REQUIRE(pass.states().blending().alpha_equation() == render::blending_equation::reverse_subtract);
}
modules::shutdown<asset_cache<binary_asset>>();
modules::shutdown<asset_cache<image_asset>>();
modules::shutdown<asset_cache<text_asset>>();
modules::shutdown<library>();
modules::shutdown<debug>();
modules::shutdown<vfs>();
}