diff --git a/example/example0.cpp b/example/example0.cpp index 2d88810..b8b8aed 100644 --- a/example/example0.cpp +++ b/example/example0.cpp @@ -8,7 +8,7 @@ constexpr int w = 640; constexpr int h = 480; -rndr::Result performBufferCopies(rndr::Application &program_gpu) +ustd::result performBufferCopies(rndr::Application &program_gpu) { const wgpu::Device &device = program_gpu.getDevice(); @@ -65,10 +65,10 @@ rndr::Result performBufferCopies(rndr::Application &program_gpu) buffer1.Destroy(); buffer2.Destroy(); - return rndr::Result::success(); + return {}; } -rndr::Result renderFrame(rndr::Application &program_gpu) +ustd::result renderFrame(rndr::Application &program_gpu) { const wgpu::Device &device = program_gpu.getDevice(); @@ -87,7 +87,7 @@ rndr::Result renderFrame(rndr::Application &program_gpu) oss << "Surface did not provide next texture. " "SurfaceGetCurrentTextureStatus:" << static_cast(surface_tex.status); - return rndr::Result::error(oss.str()); + return ustd::unexpected(oss.str()); } wgpu::Texture next_tex_src = surface_tex.texture; @@ -100,7 +100,7 @@ rndr::Result renderFrame(rndr::Application &program_gpu) wgpu::TextureView next_tex = next_tex_src.CreateView(&next_tex_descriptor); if (!next_tex) { - return rndr::Result::error("Cannot acquire next swap chain texture"); + return ustd::unexpected("Cannot acquire next swap chain texture"); } /* create the command encoder */ @@ -150,31 +150,31 @@ rndr::Result renderFrame(rndr::Application &program_gpu) /* finally, present the next texture */ program_gpu.getSurface().Present(); - return rndr::Result::success(); + return {}; } int main() { rndr::Application program_gpu; - rndr::Result init_result = program_gpu.initialize(); + ustd::result init_result = program_gpu.initialize(); if (!init_result.ok()) { std::cerr << "Initialization failed for reason: " << init_result; return 1; } - rndr::Result buffer_copy_result = performBufferCopies(program_gpu); + ustd::result buffer_copy_result = performBufferCopies(program_gpu); if (!buffer_copy_result) { std::cerr << buffer_copy_result << std::endl; return 1; } - rndr::Result render_result - = rndr::Result::error("Did not complete first frame."); + ustd::result render_result + = ustd::unexpected("Did not complete first frame."); do { if (glfwWindowShouldClose(program_gpu.getWindow())) { - render_result = rndr::Result::success("GLFW Requested Window Close"); + render_result = {}; break; } // Check whether the user clicked on the close button (and any other diff --git a/src/rndr/CMakeLists.txt b/src/rndr/CMakeLists.txt index 9bbd394..ed848d6 100644 --- a/src/rndr/CMakeLists.txt +++ b/src/rndr/CMakeLists.txt @@ -3,6 +3,15 @@ add_library(rndr STATIC ${CMAKE_CURRENT_SOURCE_DIR}/application.cpp ) +Include(FetchContent) +FetchContent_Declare( + ustd + GIT_REPOSITORY https://github.com/ancientjpeg/ustd + GIT_TAG main +) + +FetchContent_MakeAvailable(ustd) + add_subdirectory(math) add_subdirectory(resources) add_subdirectory(types) @@ -10,6 +19,8 @@ add_subdirectory(utils) target_include_directories(rndr PUBLIC ${PROJECT_SOURCE_DIR}/src) -target_link_libraries(rndr PUBLIC glfw3webgpu webgpu_cpp) +target_link_libraries(rndr PUBLIC glfw3webgpu webgpu_cpp ustd) + + target_compile_definitions(rndr PUBLIC RNDR_SUPPORT_DIR=\"${RNDR_SUPPORT_DIR}\") diff --git a/src/rndr/types/globals.cpp b/src/rndr/types/globals.cpp index 7e7e4f4..b52f4be 100644 --- a/src/rndr/types/globals.cpp +++ b/src/rndr/types/globals.cpp @@ -46,7 +46,7 @@ void Globals::setRequiredLimits(wgpu::Limits required_limits) required_limits_.limits = std::move(required_limits); } -Result Globals::initializeWebGPU() +ustd::result Globals::initializeWebGPU() { wgpu::InstanceDescriptor instance_desc = {}; instance_desc.features.timedWaitAnyEnable = true; @@ -54,14 +54,14 @@ Result Globals::initializeWebGPU() instance_ = CreateInstance(&instance_desc); if (instance_.Get() == nullptr) { - return Result::error("Failed to retrieve instance"); + return ustd::unexpected("Failed to retrieve instance"); } surface_ = wgpu::Surface::Acquire(glfwGetWGPUSurface(instance_.Get(), window_)); if (surface_.Get() == nullptr) { - return Result::error("Failed to retrieve surface"); + return ustd::unexpected("Failed to retrieve surface"); } /* Request adapter */ @@ -87,7 +87,7 @@ Result Globals::initializeWebGPU() blockOnFuture(adapter_wait_future); if (adapter_.Get() == nullptr) { - return Result::error("Failed to retrieve adapter"); + return ustd::unexpected("Failed to retrieve adapter"); } /* Get features */ @@ -100,8 +100,8 @@ Result Globals::initializeWebGPU() for (wgpu::FeatureName feature : required_features_) { if (std::find(features_.begin(), features_.end(), feature) == features_.end()) { - return Result::error("Feature with code" + std::to_string((int)feature) - + "unavailable"); + return ustd::unexpected("Feature with code" + std::to_string((int)feature) + + "unavailable"); } } @@ -111,7 +111,7 @@ Result Globals::initializeWebGPU() if (!helpers::limits_supported(supported_limits.limits, required_limits_.limits)) { - return Result::error("Cannot support required limits"); + return ustd::unexpected("Cannot support required limits"); } /* Request device */ @@ -141,11 +141,11 @@ Result Globals::initializeWebGPU() blockOnFuture(device_future); if (device_.Get() == nullptr) { - return Result::error("Failed to retrieve device"); + return ustd::unexpected("Failed to retrieve device"); } if (!error_msg.empty()) { - return Result::error(error_msg); + return ustd::unexpected(error_msg); } /* set default callbacks */ @@ -163,10 +163,10 @@ Result Globals::initializeWebGPU() surface_.Configure(&surface_config); - return Result::success(); + return {}; } -Result Globals::initializeGLFW() +ustd::result Globals::initializeGLFW() { /* GLFW init */ glfwInitHint(GLFW_CLIENT_API, GLFW_NO_API); @@ -174,13 +174,13 @@ Result Globals::initializeGLFW() glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); window_ = glfwCreateWindow(width_, height_, "RNDR", nullptr, nullptr); if (window_ == nullptr) { - return Result::error("Failed to create GLFW window"); + return ustd::unexpected("Failed to create GLFW window"); } - return Result::success(); + return {}; } -Result Globals::initialize(int width, int height) +ustd::result Globals::initialize(int width, int height) { width_ = width; height_ = height; @@ -195,7 +195,7 @@ Result Globals::initialize(int width, int height) initialized_ = true; - return Result::success(); + return {}; } bool Globals::blockOnFuture(wgpu::Future future) diff --git a/src/rndr/types/globals.h b/src/rndr/types/globals.h index 2eaf573..427dfd2 100644 --- a/src/rndr/types/globals.h +++ b/src/rndr/types/globals.h @@ -12,7 +12,7 @@ #ifndef RNDR_GLOBALS_H_ #define RNDR_GLOBALS_H_ -#include "rndr/types/types.h" +#include "ustd/expected.h" #include #include @@ -43,12 +43,12 @@ class Globals { * @param width Initial width of the screen, in pixels * @param height Initial height of the screen, in pixels */ - [[nodiscard]] Result initialize(int width = 640, int height = 480); - bool isInitialized(); + [[nodiscard]] ustd::result initialize(int width = 640, int height = 480); + bool isInitialized(); - const wgpu::Device &getDevice(); - const wgpu::Limits &getLimits(); - const wgpu::Surface &getSurface(); + const wgpu::Device &getDevice(); + const wgpu::Limits &getLimits(); + const wgpu::Surface &getSurface(); const std::vector &getFeatures(); const wgpu::Queue &getQueue(); GLFWwindow *getWindow(); @@ -82,10 +82,10 @@ class Globals { int height_ = 0; private: - Result initializeWebGPU(); - Result initializeGLFW(); + ustd::result initializeWebGPU(); + ustd::result initializeGLFW(); - bool initialized_ = false; + bool initialized_ = false; }; class GlobalAccess { diff --git a/src/rndr/types/types.h b/src/rndr/types/types.h index cac6f4b..e7846a3 100644 --- a/src/rndr/types/types.h +++ b/src/rndr/types/types.h @@ -20,62 +20,6 @@ #include namespace rndr { -/********* Error Type **********/ -template -struct is_string - : public std::disjunction>, - std::is_same>, - std::is_same>> {}; - -struct Result { - - static Result success(std::string message = {}) - { - return Result(true, message); - } - - static Result error(std::string message) - { - return Result(false, message); - } - - friend std::ostream &operator<<(std::ostream &os, const Result &result) - { - const char *msg = result.success_ ? "Succeeded" : "Failed"; - os << msg; - if (!result.message_.empty()) { - os << " with message: " << result.message_; - } - else { - os << " with no message"; - } - return os; - } - - std::string message() - { - return message_; - } - - bool ok() const - { - return success_; - } - - operator bool() const - { - return ok(); - } - -private: - Result(bool success, std::string message) - : success_(success), message_(message) - { - } - - bool success_ = false; - std::string message_; -}; /********** Mesh Data **********/ struct MeshData { diff --git a/src/rndr/utils/CMakeLists.txt b/src/rndr/utils/CMakeLists.txt index a4cca7c..9d27d9c 100644 --- a/src/rndr/utils/CMakeLists.txt +++ b/src/rndr/utils/CMakeLists.txt @@ -1,4 +1,5 @@ target_sources(rndr PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/helpers.h ${CMAKE_CURRENT_SOURCE_DIR}/helpers.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/result.h ) diff --git a/src/rndr/utils/result.h b/src/rndr/utils/result.h new file mode 100644 index 0000000..bcf253a --- /dev/null +++ b/src/rndr/utils/result.h @@ -0,0 +1,117 @@ +/** + * @file result.h + * @author Jackson Kaplan (jackson@minimal.audio) + * @date 2024-11-07 + * Copyright © 2024 Minimal. All rights reserved. + */ + +#ifndef RNDR_RESULT_H_ +#define RNDR_RESULT_H_ + +#include +#include +inline std::variant v; + +namespace rndr { +struct unexpected { + unexpected(std::string message) : msg(message) + { + } + std::string msg; +}; + +class ResultBase { +public: + virtual std::string message() const = 0; + + bool ok() const + { + return success_; + } + + operator bool() const + { + return ok(); + } + +protected: + ResultBase(bool success) : success_(success) + { + } + + bool success_; +}; + +template +class expected final : public ResultBase { + +public: + using type = T; + + expected(T value) : ResultBase(true), value_(std::in_place_index<1>, value) + { + } + + expected(unexpected failure) + : ResultBase(false), value_(std::in_place_index<0>, failure.msg) + { + } + + std::string message() const override + { + return value_.index() == 0 ? std::get<0>(value_) : ""; + } + + T &operator*() + { + return std::get<1>(value_); + } + +private: + std::variant value_; +}; + +template <> +class expected final : public ResultBase { + +public: + using type = void; + + expected() : ResultBase(true) + { + } + + expected(unexpected failure) : ResultBase(false), message_(failure.msg) + { + } + + std::string message() const override + { + return message_; + } + +private: + std::string message_; +}; + +template +inline std::ostream &operator<<(std::ostream &os, const expected &result) +{ + const char *msg = result.ok() ? "Succeeded" : "Failed"; + os << msg; + const auto message = result.message(); + if (!result.ok()) { + if (!message.empty()) { + os << " with message: " << message; + } + else { + os << " with no message"; + } + } + return os; +} + +using result = expected; + +} // namespace rndr +#endif diff --git a/tests/sanity/application.tests.cpp b/tests/sanity/application.tests.cpp index 20dc67b..454eb58 100644 --- a/tests/sanity/application.tests.cpp +++ b/tests/sanity/application.tests.cpp @@ -1,12 +1,13 @@ #include "rndr/application.h" +#include "ustd/expected.h" #include #include /** TODO unhide this test when we are able to initialize without a surface */ TEST_CASE("Application initializes and destructs without fault", "[.sanity]") { - auto app = std::make_unique(); - rndr::Result init_result = app->initialize(); + auto app = std::make_unique(); + ustd::expected init_result = app->initialize(); INFO("Failed to initialize: " << init_result); REQUIRE(!init_result.ok()); REQUIRE(app->isInitialized());