diff --git a/CMakeLists.txt b/CMakeLists.txt index 47b18a9..27942e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,9 +38,8 @@ add_library(blah src/streams/memorystream.cpp src/streams/stream.cpp - src/internal/graphics_gl.cpp - src/internal/graphics_d3d11.cpp - src/internal/graphics_dummy.cpp + src/internal/renderer_opengl.cpp + src/internal/renderer_d3d11.cpp src/internal/platform_sdl2.cpp src/internal/platform_win32.cpp ) @@ -55,22 +54,24 @@ target_include_directories(blah # Platform Variables set(BLAH_PLATFORM_SDL2 true CACHE BOOL "Use SDL2 Platform Backend") set(BLAH_PLATFORM_WIN32 false CACHE BOOL "Use Win32 Platform Backend") -set(BLAH_GRAPHICS_OPENGL true CACHE BOOL "Use OpenGL Graphics Backend") -set(BLAH_GRAPHICS_D3D11 false CACHE BOOL "Use D3D11 Graphics Backend") +set(BLAH_RENDERER_OPENGL true CACHE BOOL "Make OpenGL Renderer available") +if (WIN32) + set(BLAH_RENDERER_D3D11 true CACHE BOOL "Make D3D11 Renderer available") +else() + set(BLAH_RENDERER_D3D11 false CACHE BOOL "Make D3D11 Renderer available") +endif() set(LIBS "") -# use the OpenGL Graphics Backend -if (BLAH_GRAPHICS_OPENGL) +# use the OpenGL Renderer Backend +if (BLAH_RENDERER_OPENGL) + add_compile_definitions(BLAH_RENDERER_OPENGL) +endif() - add_compile_definitions(BLAH_GRAPHICS_OPENGL) - -# use the D3D11 Graphics Backend -elseif (BLAH_GRAPHICS_D3D11) - - add_compile_definitions(BLAH_GRAPHICS_D3D11) +# use the D3D11 Renderer Backend +if (BLAH_RENDERER_D3D11) + add_compile_definitions(BLAH_RENDERER_D3D11) set(LIBS ${LIBS} d3d11.lib dxguid.lib D3Dcompiler.lib) - endif() # use the SDL2 Platform Backend @@ -91,7 +92,7 @@ if (BLAH_PLATFORM_SDL2) FetchContent_Declare( SDL2 GIT_REPOSITORY https://github.com/libsdl-org/SDL - GIT_TAG release-2.0.18 # grab latest stable release + GIT_TAG release-2.0.20 # grab latest stable release GIT_PROGRESS TRUE ) FetchContent_MakeAvailable(SDL2) diff --git a/README.md b/README.md index ae8c7ed..c66374c 100644 --- a/README.md +++ b/README.md @@ -9,10 +9,10 @@ A small 2D C++ Game Framework, using few dependencies and simple code to mainain - [SDL2](https://github.com/NoelFB/blah/blob/master/src/internal/platform_sdl2.cpp) can be enabled in CMake with `BLAH_PLATFORM_SDL2` (default) - [WIN32](https://github.com/NoelFB/blah/blob/master/src/internal/platform_win32.cpp) (UNFINISHED) can be enabled in CMake with `BLAH_PLATFORM_WIN32` - Additional platforms can be added by implementing the [Platform Backend](https://github.com/NoelFB/blah/blob/master/src/internal/platform.h) - - A single *Graphics* implementation must be enabled: - - [OpenGL](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_gl.cpp) can be enabled in CMake with `BLAH_GRAPHICS_OPENGL` (default) - - [D3D11](https://github.com/NoelFB/blah/blob/master/src/internal/graphics_d3d11.cpp) can be enabled in CMake with `BLAH_GRAPHICS_D3D11` - - Additional graphics can be added by implementing the [Graphics Backend](https://github.com/NoelFB/blah/blob/master/src/internal/graphics.h) + - At least one *Renderer* implementation must be enabled: + - [OpenGL](https://github.com/NoelFB/blah/blob/master/src/internal/renderer_gl.cpp) can be enabled in CMake with `BLAH_RENDERER_OPENGL` + - [D3D11](https://github.com/NoelFB/blah/blob/master/src/internal/renderer_d3d11.cpp) can be enabled in CMake with `BLAH_RENDERER_D3D11` + - Additional renderers can be added by implementing the [Renderer Backend](https://github.com/NoelFB/blah/blob/master/src/internal/renderer.h) #### notes - There's no Shader abstraction, so the [Sprite Batcher](https://github.com/NoelFB/blah/blob/master/src/graphics/batch.cpp) has hard-coded GLSL/HLSL. This will need to change. @@ -27,39 +27,27 @@ A small 2D C++ Game Framework, using few dependencies and simple code to mainain using namespace Blah; Batch batch; -TextureRef tex; - -void startup() -{ - tex = Texture::create("player.png"); -} - -void render() -{ - App::backbuffer->clear(Color::black); - - auto center = Vec2f(App::backbuffer->width(), App::backbuffer->height()) / 2; - auto rotation = Time::seconds * Calc::TAU; - auto transform = Mat3x2f::create_transform(center, Vec2f::zero, Vec2f::one, rotation); - - batch.push_matrix(transform); - batch.rect(Rectf(-32, -32, 64, 64), Color::red); - batch.tex(tex, Vec2f(64, 0), Color::white); - batch.pop_matrix(); - - batch.render(); - batch.clear(); -} int main() { Config config; config.name = "blah app"; - config.width = 1280; - config.height = 720; - config.on_startup = startup; - config.on_render = render; - + config.on_render = []() + { + App::backbuffer()->clear(Color::black); + + auto center = App::get_backbuffer_size() / 2; + auto rotation = Time::seconds * Calc::TAU; + auto transform = Mat3x2f::create_transform(center, Vec2f::zero, Vec2f::one, rotation); + + batch.push_matrix(transform); + batch.rect(Rectf(-32, -32, 64, 64), Color::red); + batch.pop_matrix(); + + batch.render(); + batch.clear(); + }; + App::run(&config); return 0; } diff --git a/include/blah/app.h b/include/blah/app.h index b4c2a22..f0c6bdc 100644 --- a/include/blah/app.h +++ b/include/blah/app.h @@ -10,74 +10,20 @@ namespace Blah // Application Logging Functions using AppLogFn = Func; - // Application Configuration - struct Config - { - // Application name. - // This has no default and must be set. - const char* name; - - // Starting width, in pixels. - // Depending on the OS DPI, the true window size may be a multiple of this. - // This has no default and must be set. - int width; - - // Starting height, in pixels. - // Depending on the OS DPI, the true window size may be a multiple of this. - // This has no default and must be set. - int height; - - // maximum updates to run before "giving up" and reducing frame rate. - // this avoids the 'spiral of death'. - // defaults to 5. - int max_updates; - - // target framerate. - // defaults to 60. - int target_framerate; - - // Callback on application startup - // Defaults to nothing. - AppEventFn on_startup; - - // Callback on application shutdown - // Defaults to nothing. - AppEventFn on_shutdown; - - // Callback on application update - // Defaults to nothing. - AppEventFn on_update; - - // Callback on application render - // Defaults to nothing. - AppEventFn on_render; - - // Callback when the user has requested the application close. - // For example, pressing the Close button, ALT+F4, etc - // By default this calls `App::exit()` - AppEventFn on_exit_request; - - // Callback when the application logs info/warning/errors - // Defaults to printf. - AppLogFn on_log; - - // Default config setup - Config(); - }; - - // Renderer the Application is using - enum class Renderer + // Type of Renderer the Application is using + enum class RendererType { None = -1, OpenGL, D3D11, - Metal, - Count }; - // Features available on the current Renderer + // Renderer Information struct RendererFeatures { + // The type of Renderer being used + RendererType type = RendererType::None; + // Whether Mesh Instancing is available bool instancing = false; @@ -89,6 +35,55 @@ namespace Blah int max_texture_size = 0; }; + // Application Configuration + struct Config + { + // Application name. + const char* name = "blah"; + + // Which renderer to use + // Default depends on the Platform + RendererType renderer_type = RendererType::None; + + // Starting width, in pixels. + // Depending on the OS DPI, the true window size may be a multiple of this. + int width = 1280; + + // Starting height, in pixels. + // Depending on the OS DPI, the true window size may be a multiple of this. + int height = 720; + + // maximum updates to run before "giving up" and reducing frame rate. + // this avoids the 'spiral of death'. + // defaults to 5. + int max_updates = 5; + + // target framerate. + // defaults to 60. + int target_framerate = 60; + + // Callback on application startup + AppEventFn on_startup = nullptr; + + // Callback on application shutdown + AppEventFn on_shutdown = nullptr; + + // Callback on application update + AppEventFn on_update = nullptr; + + // Callback on application render + AppEventFn on_render = nullptr; + + // Callback when the user has requested the application close. + // For example, pressing the Close button, ALT+F4, etc + // By default this calls `App::exit()` + AppEventFn on_exit_request = nullptr; + + // Callback when the application logs info/warning/errors + // Defaults to printf. + AppLogFn on_log = nullptr; + }; + // Forward declare Target for the BackBuffer class Target; using TargetRef = Ref; @@ -119,31 +114,22 @@ namespace Blah // Sets the Window Title void set_title(const char* title); - // Gets the Window Position + // Gets the Window Position in Screen Coordinates Point get_position(); - // Sets the Window Position + // Sets the Window Position in Screen Coordinates void set_position(Point point); - // Gets the Window Size + // Gets the size of the Window in Screen Coordinates. + // On High DPI displays this may not be 1:1 with pixels. + // For the size in pixels, use App::get_backbuffer_size() Point get_size(); - // Sets the Window Size + // Sets the Window Size in Screen Coordinates void set_size(Point point); - // Gets the width of the window - int width(); - - // Gets the height of the window - int height(); - - // Gets the drawable width of the window, in pixels. - // This may differ from the width when on platforms with High DPI Displays. - int draw_width(); - - // Gets the drawable height of the window, in pixels. - // This may differ from the height when on platforms with High DPI Displays. - int draw_height(); + // Gets the size of the BackBuffer, in pixels + Point get_backbuffer_size(); // Gets the content scale based on the platform. // macOS is usually 2.0, other platforms vary. @@ -156,14 +142,11 @@ namespace Blah // Otherwise this function does nothing. void fullscreen(bool enabled); - // Returns the Rendering API in use - Renderer renderer(); - // Retrieves the Renderer Features - const RendererFeatures& renderer_features(); + const RendererFeatures& renderer(); - // Reference to the window's back buffer - extern const TargetRef backbuffer; + // Gets the BackBuffer + const TargetRef& backbuffer(); } namespace System diff --git a/include/blah/graphics/batch.h b/include/blah/graphics/batch.h index 2003e95..050f06e 100644 --- a/include/blah/graphics/batch.h +++ b/include/blah/graphics/batch.h @@ -128,7 +128,7 @@ namespace Blah void set_sampler(const TextureSampler& sampler); // Draws the batch to the given target - void render(const TargetRef& target = App::backbuffer); + void render(const TargetRef& target = nullptr); // Draws the batch to the given target, with the provided matrix void render(const TargetRef& target, const Mat4x4f& matrix); diff --git a/src/app.cpp b/src/app.cpp index 9bc327e..dbec180 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -3,7 +3,7 @@ #include #include #include "internal/platform.h" -#include "internal/graphics.h" +#include "internal/renderer.h" #include "internal/input.h" #ifdef __EMSCRIPTEN__ @@ -13,286 +13,7 @@ using namespace Blah; -Config::Config() -{ - name = nullptr; - width = 0; - height = 0; - target_framerate = 60; - max_updates = 5; - - on_startup = nullptr; - on_shutdown = nullptr; - on_update = nullptr; - on_render = nullptr; - on_exit_request = App::exit; - on_log = nullptr; -} - -namespace -{ - Config app_config; - bool app_is_running = false; - bool app_is_exiting = false; - u64 time_last; - u64 time_accumulator = 0; - - void app_iterate() - { - // update at a fixed timerate - // TODO: allow a non-fixed step update? - { - u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second); - u64 time_curr = Platform::ticks(); - u64 time_diff = time_curr - time_last; - time_last = time_curr; - time_accumulator += time_diff; - - // do not let us run too fast - while (time_accumulator < time_target) - { - int milliseconds = (int)(time_target - time_accumulator) / (Time::ticks_per_second / 1000); - Platform::sleep(milliseconds); - - time_curr = Platform::ticks(); - time_diff = time_curr - time_last; - time_last = time_curr; - time_accumulator += time_diff; - } - - // Do not allow us to fall behind too many updates - // (otherwise we'll get spiral of death) - u64 time_maximum = app_config.max_updates * time_target; - if (time_accumulator > time_maximum) - time_accumulator = time_maximum; - - // do as many updates as we can - while (time_accumulator >= time_target) - { - time_accumulator -= time_target; - - Time::delta = (1.0f / app_config.target_framerate); - - if (Time::pause_timer > 0) - { - Time::pause_timer -= Time::delta; - if (Time::pause_timer <= -0.0001) - Time::delta = -Time::pause_timer; - else - continue; - } - - Time::previous_ticks = Time::ticks; - Time::ticks += time_target; - Time::previous_seconds = Time::seconds; - Time::seconds += Time::delta; - - Input::update_state(); - Platform::update(Input::state); - Input::update_bindings(); - Graphics::update(); - - if (app_config.on_update != nullptr) - app_config.on_update(); - } - } - - // render - { - Graphics::before_render(); - - if (app_config.on_render != nullptr) - app_config.on_render(); - - Graphics::after_render(); - Platform::present(); - } - } - -} - -bool App::run(const Config* c) -{ - BLAH_ASSERT(!app_is_running, "The Application is already running"); - BLAH_ASSERT(c != nullptr, "The Application requires a valid Config"); - BLAH_ASSERT(c->name != nullptr, "The Application Name cannot be null"); - BLAH_ASSERT(c->width > 0 && c->height > 0, "The Width and Height must be larget than 0"); - BLAH_ASSERT(c->max_updates > 0, "Max Updates must be >= 1"); - BLAH_ASSERT(c->target_framerate > 0, "Target Framerate must be >= 1"); - - app_config = *c; - app_is_running = true; - app_is_exiting = false; - - // initialize the system - if (!Platform::init(app_config)) - { - Log::error("Failed to initialize Platform module"); - return false; - } - - // initialize graphics - if (!Graphics::init()) - { - Log::error("Failed to initialize Graphics module"); - return false; - } - - // input - Input::init(); - - // prepare by updating input & platform once - Input::update_state(); - Platform::update(Input::state); - - // startup - if (app_config.on_startup != nullptr) - app_config.on_startup(); - - time_last = Platform::ticks(); - time_accumulator = 0; - - // display window - Platform::ready(); - - // Begin main loop - // Emscripten requires the main loop be separated into its own call - -#ifdef __EMSCRIPTEN__ - emscripten_set_main_loop(app_iterate, 0, 1); -#else - while (!app_is_exiting) - app_iterate(); -#endif - - // shutdown - if (app_config.on_shutdown != nullptr) - app_config.on_shutdown(); - - Graphics::shutdown(); - Platform::shutdown(); - - // clear static state - app_is_running = false; - app_is_exiting = false; - - Time::ticks = 0; - Time::seconds = 0; - Time::previous_ticks = 0; - Time::previous_seconds = 0; - Time::delta = 0; - - return true; -} - -void App::exit() -{ - if (!app_is_exiting && app_is_running) - app_is_exiting = true; -} - -const Config& App::config() -{ - return app_config; -} - -const char* App::path() -{ - return Platform::app_path(); -} - -const char* App::user_path() -{ - return Platform::user_path(); -} - -const char* App::get_title() -{ - return Platform::get_title(); -} - -void App::set_title(const char* title) -{ - Platform::set_title(title); -} - -Point App::get_position() -{ - Point result; - Platform::get_position(&result.x, &result.y); - return result; -} - -void App::set_position(Point point) -{ - Platform::set_position(point.x, point.y); -} - -Point App::get_size() -{ - Point result; - Platform::get_size(&result.x, &result.y); - return result; -} - -void App::set_size(Point point) -{ - Platform::set_size(point.x, point.y); -} - -int App::width() -{ - return get_size().x; -} - -int App::height() -{ - return get_size().y; -} - -int App::draw_width() -{ - int w, h; - Platform::get_draw_size(&w, &h); - return w; -} - -int App::draw_height() -{ - int w, h; - Platform::get_draw_size(&w, &h); - return h; -} - -float App::content_scale() -{ - return Platform::get_content_scale(); -} - -void App::fullscreen(bool enabled) -{ - Platform::set_fullscreen(enabled); -} - -bool App::focused() -{ - return Platform::get_focused(); -} - -Renderer App::renderer() -{ - return Graphics::renderer(); -} - -const RendererFeatures& Blah::App::renderer_features() -{ - return Graphics::features(); -} - -void System::open_url(const char* url) -{ - Platform::open_url(url); -} +#define BLAH_ASSERT_RUNNING() BLAH_ASSERT(app_is_running, "The App is not running (call App::run)") namespace { @@ -316,20 +37,315 @@ namespace int width() const override { - return App::draw_width(); + int w, h; + Platform::get_draw_size(&w, &h); + return w; } int height() const override { - return App::draw_height(); + int w, h; + Platform::get_draw_size(&w, &h); + return h; } void clear(Color color, float depth, u8 stencil, ClearMask mask) override { - Graphics::clear_backbuffer(color, depth, stencil, mask); + if (Renderer::instance) + Renderer::instance->clear_backbuffer(color, depth, stencil, mask); } }; + Config app_config; + bool app_is_running = false; + bool app_is_exiting = false; + u64 app_time_last; + u64 app_time_accumulator = 0; + TargetRef app_backbuffer; + + void app_iterate() + { + // update at a fixed timerate + // TODO: allow a non-fixed step update? + { + u64 time_target = (u64)((1.0 / app_config.target_framerate) * Time::ticks_per_second); + u64 time_curr = Platform::ticks(); + u64 time_diff = time_curr - app_time_last; + app_time_last = time_curr; + app_time_accumulator += time_diff; + + // do not let us run too fast + while (app_time_accumulator < time_target) + { + int milliseconds = (int)(time_target - app_time_accumulator) / (Time::ticks_per_second / 1000); + Platform::sleep(milliseconds); + + time_curr = Platform::ticks(); + time_diff = time_curr - app_time_last; + app_time_last = time_curr; + app_time_accumulator += time_diff; + } + + // Do not allow us to fall behind too many updates + // (otherwise we'll get spiral of death) + u64 time_maximum = app_config.max_updates * time_target; + if (app_time_accumulator > time_maximum) + app_time_accumulator = time_maximum; + + // do as many updates as we can + while (app_time_accumulator >= time_target) + { + app_time_accumulator -= time_target; + + Time::delta = (1.0f / app_config.target_framerate); + + if (Time::pause_timer > 0) + { + Time::pause_timer -= Time::delta; + if (Time::pause_timer <= -0.0001) + Time::delta = -Time::pause_timer; + else + continue; + } + + Time::previous_ticks = Time::ticks; + Time::ticks += time_target; + Time::previous_seconds = Time::seconds; + Time::seconds += Time::delta; + + Input::update_state(); + Platform::update(Input::state); + Input::update_bindings(); + Renderer::instance->update(); + + if (app_config.on_update != nullptr) + app_config.on_update(); + } + } + + // render + { + Renderer::instance->before_render(); + + if (app_config.on_render != nullptr) + app_config.on_render(); + + Renderer::instance->after_render(); + Platform::present(); + } + } + } -const TargetRef App::backbuffer = TargetRef(new BackBuffer()); \ No newline at end of file +bool App::run(const Config* c) +{ + BLAH_ASSERT(!app_is_running, "The Application is already running"); + BLAH_ASSERT(c != nullptr, "The Application requires a valid Config"); + BLAH_ASSERT(c->name != nullptr, "The Application Name cannot be null"); + BLAH_ASSERT(c->width > 0 && c->height > 0, "The Width and Height must be larget than 0"); + BLAH_ASSERT(c->max_updates > 0, "Max Updates must be >= 1"); + BLAH_ASSERT(c->target_framerate > 0, "Target Framerate must be >= 1"); + + // copy config over + app_config = *c; + + // exit the application by default + if (!app_config.on_exit_request) + app_config.on_exit_request = App::exit; + + // default renderer type + if (app_config.renderer_type == RendererType::None) + app_config.renderer_type = Renderer::default_type(); + + // default values + app_is_running = true; + app_is_exiting = false; + app_backbuffer = TargetRef(new BackBuffer()); + + // initialize the system + if (!Platform::init(app_config)) + { + Log::error("Failed to initialize Platform module"); + return false; + } + + // initialize graphics + { + // instantiate + Renderer::instance = Renderer::try_make_renderer(app_config.renderer_type); + + // wasn't able to make any + if (Renderer::instance == nullptr) + { + Log::error("Renderer implementation was not found"); + return false; + } + + if (!Renderer::instance->init()) + { + Log::error("Renderer failed to initialize"); + delete Renderer::instance; + return false; + } + } + + // input + Input::init(); + + // prepare by updating input & platform once + Input::update_state(); + Platform::update(Input::state); + + // startup + if (app_config.on_startup != nullptr) + app_config.on_startup(); + + app_time_last = Platform::ticks(); + app_time_accumulator = 0; + + // display window + Platform::ready(); + + // Begin main loop + // Emscripten requires the main loop be separated into its own call + +#ifdef __EMSCRIPTEN__ + emscripten_set_main_loop(app_iterate, 0, 1); +#else + while (!app_is_exiting) + app_iterate(); +#endif + + // shutdown + if (app_config.on_shutdown != nullptr) + app_config.on_shutdown(); + + Renderer::instance->shutdown(); + Platform::shutdown(); + + // clear static state + app_is_running = false; + app_is_exiting = false; + app_backbuffer = nullptr; + + delete Renderer::instance; + Renderer::instance = nullptr; + + Time::ticks = 0; + Time::seconds = 0; + Time::previous_ticks = 0; + Time::previous_seconds = 0; + Time::delta = 0; + + return true; +} + +void App::exit() +{ + BLAH_ASSERT_RUNNING(); + if (!app_is_exiting && app_is_running) + app_is_exiting = true; +} + +const Config& App::config() +{ + BLAH_ASSERT_RUNNING(); + return app_config; +} + +const char* App::path() +{ + BLAH_ASSERT_RUNNING(); + return Platform::app_path(); +} + +const char* App::user_path() +{ + BLAH_ASSERT_RUNNING(); + return Platform::user_path(); +} + +const char* App::get_title() +{ + BLAH_ASSERT_RUNNING(); + return Platform::get_title(); +} + +void App::set_title(const char* title) +{ + BLAH_ASSERT_RUNNING(); + Platform::set_title(title); +} + +Point App::get_position() +{ + BLAH_ASSERT_RUNNING(); + Point result; + Platform::get_position(&result.x, &result.y); + return result; +} + +void App::set_position(Point point) +{ + BLAH_ASSERT_RUNNING(); + Platform::set_position(point.x, point.y); +} + +Point App::get_size() +{ + BLAH_ASSERT_RUNNING(); + Point result; + Platform::get_size(&result.x, &result.y); + return result; +} + +void App::set_size(Point point) +{ + BLAH_ASSERT_RUNNING(); + Platform::set_size(point.x, point.y); +} + +Point App::get_backbuffer_size() +{ + BLAH_ASSERT_RUNNING(); + if (app_backbuffer) + return Point(app_backbuffer->width(), app_backbuffer->height()); + return Point(0, 0); +} + +float App::content_scale() +{ + BLAH_ASSERT_RUNNING(); + return Platform::get_content_scale(); +} + +bool App::focused() +{ + BLAH_ASSERT_RUNNING(); + return Platform::get_focused(); +} + +void App::fullscreen(bool enabled) +{ + BLAH_ASSERT_RUNNING(); + Platform::set_fullscreen(enabled); +} + +const RendererFeatures& App::renderer() +{ + BLAH_ASSERT_RUNNING(); + BLAH_ASSERT_RENDERER(); + return Renderer::instance->features; +} + +const TargetRef& App::backbuffer() +{ + BLAH_ASSERT_RUNNING(); + return app_backbuffer; +} + +void System::open_url(const char* url) +{ + BLAH_ASSERT_RUNNING(); + Platform::open_url(url); +} \ No newline at end of file diff --git a/src/graphics/batch.cpp b/src/graphics/batch.cpp index cddcafa..38c887e 100644 --- a/src/graphics/batch.cpp +++ b/src/graphics/batch.cpp @@ -118,12 +118,12 @@ namespace }; const VertexFormat format = VertexFormat( - { - { 0, VertexType::Float2, false }, - { 1, VertexType::Float2, false }, - { 2, VertexType::UByte4, true }, - { 3, VertexType::UByte4, true }, - }); + { + { 0, VertexType::Float2, false }, + { 1, VertexType::Float2, false }, + { 2, VertexType::UByte4, true }, + { 3, VertexType::UByte4, true }, + }); } namespace @@ -378,7 +378,7 @@ void Batch::set_texture(const TextureRef& texture) if (m_batch.texture != texture) { m_batch.texture = texture; - m_batch.flip_vertically = App::renderer_features().origin_bottom_left && texture && texture->is_framebuffer(); + m_batch.flip_vertically = App::renderer().origin_bottom_left && texture && texture->is_framebuffer(); } } @@ -392,13 +392,8 @@ void Batch::set_sampler(const TextureSampler& sampler) void Batch::render(const TargetRef& target) { - Point size; - if (!target) - size = Point(App::draw_width(), App::draw_height()); - else - size = Point(target->width(), target->height()); - - render(target, Mat4x4f::create_ortho_offcenter(0, (float)size.x, (float)size.y, 0, 0.01f, 1000.0f)); + TargetRef ref = (target ? target : App::backbuffer()); + render(ref, Mat4x4f::create_ortho_offcenter(0, (float)ref->width(), (float)ref->height(), 0, 0.01f, 1000.0f)); } void Batch::render(const TargetRef& target, const Mat4x4f& matrix) @@ -414,9 +409,9 @@ void Batch::render(const TargetRef& target, const Mat4x4f& matrix) if (!m_default_shader) { - if (App::renderer() == Renderer::OpenGL) + if (App::renderer().type == RendererType::OpenGL) m_default_shader = Shader::create(opengl_shader_data); - else if (App::renderer() == Renderer::D3D11) + else if (App::renderer().type == RendererType::D3D11) m_default_shader = Shader::create(d3d11_shader_data); } diff --git a/src/graphics/mesh.cpp b/src/graphics/mesh.cpp index 25d69d7..bec8450 100644 --- a/src/graphics/mesh.cpp +++ b/src/graphics/mesh.cpp @@ -1,12 +1,16 @@ #include -#include "../internal/graphics.h" +#include "../internal/renderer.h" using namespace Blah; - MeshRef Mesh::create() { - return Graphics::create_mesh(); + BLAH_ASSERT_RENDERER(); + + if (Renderer::instance) + return Renderer::instance->create_mesh(); + + return MeshRef(); } VertexFormat::VertexFormat(std::initializer_list attributes, int stride) diff --git a/src/graphics/renderpass.cpp b/src/graphics/renderpass.cpp index 9914ceb..bae713a 100644 --- a/src/graphics/renderpass.cpp +++ b/src/graphics/renderpass.cpp @@ -1,13 +1,13 @@ #include #include -#include "../internal/graphics.h" +#include "../internal/renderer.h" using namespace Blah; RenderPass::RenderPass() { blend = BlendMode::Normal; - target = App::backbuffer; + target = App::backbuffer(); mesh = MeshRef(); material = MaterialRef(); has_viewport = false; @@ -23,17 +23,21 @@ RenderPass::RenderPass() void RenderPass::perform() { + BLAH_ASSERT_RENDERER(); BLAH_ASSERT(material, "Trying to draw with an invalid Material"); BLAH_ASSERT(material->shader(), "Trying to draw with an invalid Shader"); BLAH_ASSERT(mesh, "Trying to draw with an invalid Mesh"); + if (!Renderer::instance) + return; + // copy call RenderPass pass = *this; // Validate Backbuffer if (!pass.target) { - pass.target = App::backbuffer; + pass.target = App::backbuffer(); Log::warn("Trying to draw with an invalid Target; falling back to Back Buffer"); } @@ -86,5 +90,5 @@ void RenderPass::perform() pass.scissor = pass.scissor.overlap_rect(Rectf(0, 0, draw_size.x, draw_size.y)); // perform render - Graphics::render(pass); + Renderer::instance->render(pass); } diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index cdc8738..728a111 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -1,16 +1,20 @@ #include #include -#include "../internal/graphics.h" +#include "../internal/renderer.h" using namespace Blah; ShaderRef Shader::create(const ShaderData& data) { + BLAH_ASSERT_RENDERER(); BLAH_ASSERT(data.vertex.length() > 0, "Must provide a Vertex Shader"); BLAH_ASSERT(data.fragment.length() > 0, "Must provide a Fragment Shader"); - BLAH_ASSERT(data.hlsl_attributes.size() > 0 || App::renderer() != Renderer::D3D11, "D3D11 Shaders must have hlsl_attributes assigned"); + BLAH_ASSERT(data.hlsl_attributes.size() > 0 || App::renderer().type != RendererType::D3D11, "D3D11 Shaders must have hlsl_attributes assigned"); - auto shader = Graphics::create_shader(&data); + ShaderRef shader; + + if (Renderer::instance) + shader = Renderer::instance->create_shader(&data); // validate the shader if (shader) diff --git a/src/graphics/target.cpp b/src/graphics/target.cpp index 3fdaab9..4245911 100644 --- a/src/graphics/target.cpp +++ b/src/graphics/target.cpp @@ -1,5 +1,5 @@ #include -#include "../internal/graphics.h" +#include "../internal/renderer.h" using namespace Blah; @@ -10,6 +10,7 @@ TargetRef Target::create(int width, int height) TargetRef Target::create(int width, int height, const AttachmentFormats& textures) { + BLAH_ASSERT_RENDERER(); BLAH_ASSERT(width > 0 && height > 0, "Target width and height must be larger than 0"); BLAH_ASSERT(textures.size() > 0, "At least one texture must be provided"); @@ -29,7 +30,10 @@ TargetRef Target::create(int width, int height, const AttachmentFormats& texture BLAH_ASSERT(depth_count <= 1, "Target can only have 1 Depth/Stencil Texture"); BLAH_ASSERT(color_count <= Attachments::capacity - 1, "Exceeded maximum Color texture count"); - return Graphics::create_target(width, height, textures.data(), textures.size()); + if (Renderer::instance) + return Renderer::instance->create_target(width, height, textures.data(), textures.size()); + + return TargetRef(); } TextureRef& Target::texture(int index) diff --git a/src/graphics/texture.cpp b/src/graphics/texture.cpp index f20634f..c988c0a 100644 --- a/src/graphics/texture.cpp +++ b/src/graphics/texture.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "../internal/graphics.h" +#include "../internal/renderer.h" using namespace Blah; @@ -13,15 +13,21 @@ TextureRef Texture::create(const Image& image) TextureRef Texture::create(int width, int height, TextureFormat format, unsigned char* data) { + BLAH_ASSERT_RENDERER(); BLAH_ASSERT(width > 0 && height > 0, "Texture width and height must be larger than 0"); BLAH_ASSERT((int)format > (int)TextureFormat::None && (int)format < (int)TextureFormat::Count, "Invalid texture format"); - auto tex = Graphics::create_texture(width, height, format); + if (Renderer::instance) + { + auto tex = Renderer::instance->create_texture(width, height, format); - if (tex && data != nullptr) - tex->set_data(data); + if (tex && data != nullptr) + tex->set_data(data); - return tex; + return tex; + } + + return TextureRef(); } TextureRef Texture::create(Stream& stream) diff --git a/src/input.cpp b/src/input.cpp index 261cf25..00610d2 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -5,6 +5,7 @@ #include #include "internal/input.h" #include "internal/platform.h" +#include #include using namespace Blah; @@ -124,8 +125,8 @@ void MouseState::on_move(const Vec2f& pos, const Vec2f& screen_pos) position = pos; screen_position = screen_pos; - Point size = Point(App::width(), App::height()); - Point draw = Point(App::draw_width(), App::draw_height()); + Point size = App::get_size(); + Point draw = Point(App::backbuffer()->width(), App::backbuffer()->height()); draw_position.x = (position.x / (float)size.x) * draw.x; draw_position.y = (position.y / (float)size.y) * draw.y; diff --git a/src/internal/graphics.h b/src/internal/graphics.h deleted file mode 100644 index ad7098d..0000000 --- a/src/internal/graphics.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Blah -{ - // Graphics backend API used for rendering. - // All rendering ends up going through here. - namespace Graphics - { - // Initializes the graphics backend - bool init(); - - // Shuts down the graphics backend - void shutdown(); - - // Returns info about the renderer - const RendererFeatures& features(); - - // Returns the renderer type - Renderer renderer(); - - // Called once per frame - void update(); - - // Called before rendering begins - void before_render(); - - // Called after renderings ends - void after_render(); - - // Performs a draw call - void render(const RenderPass& pass); - - // Clears the backbuffer - void clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask); - - // Creates a new Texture. - // if the Texture is invalid, this should return an empty reference. - TextureRef create_texture(int width, int height, TextureFormat format); - - // Creates a new Target. - // if the Target is invalid, this should return an empty reference. - TargetRef create_target(int width, int height, const TextureFormat* attachments, int attachment_count); - - // Creates a new Shader. - // if the Shader is invalid, this should return an empty reference. - ShaderRef create_shader(const ShaderData* data); - - // Creates a new Mesh. - // if the Mesh is invalid, this should return an empty reference. - MeshRef create_mesh(); - } -} \ No newline at end of file diff --git a/src/internal/graphics_dummy.cpp b/src/internal/graphics_dummy.cpp deleted file mode 100644 index 8ce436c..0000000 --- a/src/internal/graphics_dummy.cpp +++ /dev/null @@ -1,215 +0,0 @@ -#if !(defined(BLAH_GRAPHICS_OPENGL) || defined(BLAH_GRAPHICS_D3D11)) - -#include "graphics.h" -#include "platform.h" -#include - -namespace Blah -{ - class Dummy_Texture : public Texture - { - private: - int m_width; - int m_height; - TextureFormat m_format; - bool m_framebuffer; - - public: - - Dummy_Texture(int width, int height, TextureFormat format, bool framebuffer) - { - m_width = width; - m_height = height; - m_format = format; - m_framebuffer = framebuffer; - } - - virtual int width() const override - { - return m_width; - } - - virtual int height() const override - { - return m_height; - } - - virtual TextureFormat format() const override - { - return m_format; - } - - virtual void set_data(unsigned char* data) override - { - - } - - virtual void get_data(unsigned char* data) override - { - - } - - virtual bool is_framebuffer() const override - { - return m_framebuffer; - } - - }; - - class Dummy_Target : public Target - { - private: - Attachments m_attachments; - - public: - - Dummy_Target(int width, int height, const TextureFormat* attachments, int attachmentCount) - { - for (int i = 0; i < attachmentCount; i++) - { - m_attachments.push_back( - TextureRef(new Dummy_Texture(width, height, attachments[i], true)) - ); - } - } - - virtual Attachments& textures() override - { - return m_attachments; - } - - virtual const Attachments& textures() const override - { - return m_attachments; - } - - virtual void clear(Color color) override - { - - } - }; - - class Dummy_Shader : public Shader - { - private: - Vector m_uniforms; - - public: - Dummy_Shader(const ShaderData* data) - { - - } - - virtual Vector& uniforms() override - { - return m_uniforms; - } - - virtual const Vector& uniforms() const override - { - return m_uniforms; - } - }; - - class Dummy_Mesh : public Mesh - { - private: - i64 m_index_count = 0; - i64 m_vertex_count = 0; - i64 m_instance_count = 0; - public: - - Dummy_Mesh() - { - - } - - virtual void index_data(IndexFormat format, const void* indices, i64 count) override - { - m_index_count = count; - } - - virtual void vertex_data(const VertexFormat& format, const void* vertices, i64 count) override - { - m_vertex_count = count; - } - - virtual void instance_data(const VertexFormat& format, const void* instances, i64 count) override - { - m_instance_count = count; - } - - virtual i64 index_count() const override - { - return m_index_count; - } - - virtual i64 vertex_count() const override - { - return m_vertex_count; - } - - virtual i64 instance_count() const override - { - return m_instance_count; - } - }; - - bool Graphics::init() - { - Log::info("Dummy Renderer"); - return true; - } - - Renderer Graphics::renderer() - { - return Renderer::None; - } - - void Graphics::shutdown() - { - - } - - const RendererFeatures& Graphics::features() - { - static const RendererFeatures features{ false, true, 4096 }; - return features; - } - - void Graphics::update() {} - void Graphics::before_render() {} - void Graphics::after_render() {} - - TextureRef Graphics::create_texture(int width, int height, TextureFormat format) - { - return TextureRef(new Dummy_Texture(width, height, format, false)); - } - - TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount) - { - return TargetRef(new Dummy_Target(width, height, attachments, attachmentCount)); - } - - ShaderRef Graphics::create_shader(const ShaderData* data) - { - return ShaderRef(new Dummy_Shader(data)); - } - - MeshRef Graphics::create_mesh() - { - return MeshRef(new Dummy_Mesh()); - } - - void Graphics::render(const RenderPass& pass) - { - - } - - void Graphics::clear_backbuffer(Color color) - { - - } -} - -#endif // !(defined(BLAH_GRAPHICS_OPENGL) || defined(BLAH_GRAPHICS_D3D11)) diff --git a/src/internal/platform.h b/src/internal/platform.h index 49bbfdd..2e3b2dd 100644 --- a/src/internal/platform.h +++ b/src/internal/platform.h @@ -42,7 +42,7 @@ namespace Blah // Sets the Application Window Position, in Screen Coordinates void set_position(int x, int y); - + // Gets whether the Window has focus bool get_focused(); @@ -97,6 +97,9 @@ namespace Blah // gets the contents of the clipboard into the given string const char* get_clipboard(); + // Tries to open a URL in a web browser + void open_url(const char* url); + // OpenGL Methods void* gl_get_func(const char* name); void* gl_context_create(); @@ -105,8 +108,5 @@ namespace Blah // D3D11 Methods void* d3d11_get_hwnd(); - - // Tries to open a URL in a web browser - void open_url(const char* url); } } \ No newline at end of file diff --git a/src/internal/platform_sdl2.cpp b/src/internal/platform_sdl2.cpp index 5f04a3f..b01dfa6 100644 --- a/src/internal/platform_sdl2.cpp +++ b/src/internal/platform_sdl2.cpp @@ -2,7 +2,7 @@ #include "platform.h" #include "input.h" -#include "graphics.h" +#include "renderer.h" #include #include #include @@ -37,7 +37,7 @@ namespace Blah char* base_path = nullptr; char* user_path = nullptr; bool displayed = false; - }; + } g_platform; // Blah SDL2 File class SDL2File : public File @@ -118,8 +118,6 @@ namespace Blah using namespace Blah; -static Blah::SDL2Platform g_platform; - bool Platform::init(const Config& config) { g_platform = SDL2Platform(); @@ -150,7 +148,7 @@ bool Platform::init(const Config& config) int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE; // enable OpenGL - if (App::renderer() == Renderer::OpenGL) + if (config.renderer_type == RendererType::OpenGL) { flags |= SDL_WINDOW_OPENGL; @@ -217,7 +215,7 @@ void Platform::ready() // enable V-Sync // TODO: // This should be a toggle or controllable in some way - if (App::renderer() == Renderer::OpenGL) + if (App::renderer().type == RendererType::OpenGL) SDL_GL_SetSwapInterval(1); #endif } @@ -454,7 +452,7 @@ void Platform::sleep(int milliseconds) void Platform::present() { - if (App::renderer() == Renderer::OpenGL) + if (App::renderer().type == RendererType::OpenGL) { SDL_GL_SwapWindow(g_platform.window); } @@ -514,7 +512,7 @@ void Platform::set_size(int width, int height) void Platform::get_draw_size(int* width, int* height) { - if (App::renderer() == Renderer::OpenGL) + if (App::renderer().type == RendererType::OpenGL) { SDL_GL_GetDrawableSize(g_platform.window, width, height); } diff --git a/src/internal/platform_win32.cpp b/src/internal/platform_win32.cpp index 5e3d355..e5f144c 100644 --- a/src/internal/platform_win32.cpp +++ b/src/internal/platform_win32.cpp @@ -1,14 +1,11 @@ #ifdef BLAH_PLATFORM_WIN32 // Note: -// This backend implementation is unfinished! -// It's missing a few things, namely: -// - Controller Support -// (And error testing) +// This is unfinished! It is missing Controller Support! #include "platform.h" #include "input.h" -#include "graphics.h" +#include "renderer.h" #include #include #include @@ -22,11 +19,20 @@ #include // for file explore #include // for known folder #include // for ticks method +#include // for XInput namespace Blah { using Duration = std::chrono::system_clock::duration; + typedef HRESULT(WINAPI* DirectInput8Create_fn)(HINSTANCE, DWORD, REFIID, LPVOID*, LPUNKNOWN); + typedef DWORD(WINAPI* XInputGetCapabilities_fn)(DWORD, DWORD, XINPUT_CAPABILITIES*); + typedef DWORD(WINAPI* XInputGetState_fn)(DWORD, XINPUT_STATE*); + typedef void*(WINAPI* wglGetProcAddress_fn)(const char*); + typedef HGLRC(WINAPI* wglCreateContext_fn)(HDC); + typedef BOOL(WINAPI* wglDeleteContext_fn)(HGLRC); + typedef BOOL(WINAPI* wglMakeCurrent_fn)(HDC, HGLRC); + // Win32 Platform State struct Win32Platform { @@ -38,14 +44,34 @@ namespace Blah RECT windowed_position; bool fullscreen = false; InputState* input_state = nullptr; + String clipboard; + + // XInput + struct + { + HMODULE dll; + XInputGetCapabilities_fn get_capabilities; + XInputGetState_fn get_state; + } xinput; + + struct Joystick + { + bool connected = false; + bool accounted = false; + GUID dinstance = GUID_NULL; + DWORD xindex = 0; + } joysticks[Input::max_controllers]; // OpenGL Methods // These are only loaded if built using the OpenGL Backend - HMODULE opengl_dll; - void* (WINAPI* wglGetProcAddress) (const char* proc); - HGLRC (WINAPI* wglCreateContext) (HDC hdc); - BOOL (WINAPI* wglDeleteContext) (HGLRC hglrc); - BOOL (WINAPI* wglMakeCurrent) (HDC hdc, HGLRC hglrc); + struct + { + HMODULE dll; + wglGetProcAddress_fn get_proc_address; + wglCreateContext_fn create_context; + wglDeleteContext_fn delete_context; + wglMakeCurrent_fn make_current; + } gl; }; // Win32 File Class @@ -53,11 +79,16 @@ namespace Blah { private: HANDLE m_handle; + LARGE_INTEGER m_size; public: Win32File(HANDLE handle) { m_handle = handle; + + LARGE_INTEGER file_size; + if (GetFileSizeEx(m_handle, &file_size)) + m_size = file_size; } ~Win32File() @@ -67,13 +98,7 @@ namespace Blah size_t length() override { - // Todo: cache this value? not sure how performant it is - - LARGE_INTEGER file_size; - if (GetFileSizeEx(m_handle, &file_size)) - return file_size.QuadPart; - - return 0; + return m_size.QuadPart; } size_t position() override @@ -149,11 +174,14 @@ namespace Blah } }; + // Main Windows Procedure callback + LRESULT CALLBACK win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + // Converts Windows scancode to Blah key Key win32_scancode_to_key(WPARAM wParam, LPARAM lParam); - // Main Windows Procedure callback - LRESULT CALLBACK win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + // Detects joysticks (connects & disconnects) + void win32_detect_joysticks(); } using namespace Blah; @@ -198,22 +226,30 @@ bool Platform::init(const Config& config) return false; } + // Setup Window Size based on content scale + { + auto scale = get_content_scale(); + int sw = (int)(App::config().width * scale); + int sh = (int)(App::config().height * scale); + set_size(sw, sh); + } + // Create the OpenGL device info - if (App::renderer() == Renderer::OpenGL) + if (config.renderer_type == RendererType::OpenGL) { // Load the DLL - g_platform.opengl_dll = LoadLibraryA("opengl32.dll"); - if (g_platform.opengl_dll == NULL) + g_platform.gl.dll = LoadLibraryA("opengl32.dll"); + if (g_platform.gl.dll == NULL) { Log::error("OpenGL Instantiation Failed - unable to fine opengl32.dll"); return false; } // Get the Windows GL functions we need - g_platform.wglGetProcAddress = (void* (WINAPI*)(const char*))GetProcAddress(g_platform.opengl_dll, "wglGetProcAddress"); - g_platform.wglCreateContext = (HGLRC(WINAPI*) (HDC))GetProcAddress(g_platform.opengl_dll, "wglCreateContext"); - g_platform.wglDeleteContext = (BOOL(WINAPI*) (HGLRC))GetProcAddress(g_platform.opengl_dll, "wglDeleteContext"); - g_platform.wglMakeCurrent = (BOOL(WINAPI*) (HDC, HGLRC))GetProcAddress(g_platform.opengl_dll, "wglMakeCurrent"); + g_platform.gl.get_proc_address = (wglGetProcAddress_fn)GetProcAddress(g_platform.gl.dll, "wglGetProcAddress"); + g_platform.gl.create_context = (wglCreateContext_fn)GetProcAddress(g_platform.gl.dll, "wglCreateContext"); + g_platform.gl.delete_context = (wglDeleteContext_fn)GetProcAddress(g_platform.gl.dll, "wglDeleteContext"); + g_platform.gl.make_current = (wglMakeCurrent_fn)GetProcAddress(g_platform.gl.dll, "wglMakeCurrent"); // TODO: // Allow the user to apply (some of) these values before instantiation. @@ -250,8 +286,25 @@ bool Platform::init(const Config& config) SetPixelFormat(hdc, pixel_format, &pfd); } - // Reset our game timer - g_platform.start_time = std::chrono::system_clock::now().time_since_epoch(); + // xinput api + { + const char* dlls[] = { "xinput1_4.dll", "xinput1_3.dll", "xinput9_1_0.dll", "xinput1_2.dll", "xinput1_1.dll", NULL }; + + for (int i = 0; dlls[i]; i++) + { + g_platform.xinput.dll = LoadLibraryA(dlls[i]); + + if (g_platform.xinput.dll) + { + g_platform.xinput.get_capabilities = (XInputGetCapabilities_fn)GetProcAddress(g_platform.xinput.dll, "XInputGetCapabilities"); + g_platform.xinput.get_state = (XInputGetState_fn)GetProcAddress(g_platform.xinput.dll, "XInputGetState"); + break; + } + } + + if (!g_platform.xinput.dll) + Log::warn("Failed to find XInput dll; No Controller Support"); + } // Get Working Directory { @@ -283,6 +336,9 @@ bool Platform::init(const Config& config) CoTaskMemFree(path); } + // Reset our game timer + g_platform.start_time = std::chrono::system_clock::now().time_since_epoch(); + // Not currently fullscreen g_platform.fullscreen = false; @@ -292,20 +348,18 @@ bool Platform::init(const Config& config) void Platform::ready() { - // Setup Window Size - { - auto scale = get_content_scale(); - int sw = (int)(App::config().width * scale); - int sh = (int)(App::config().height * scale); - set_size(sw, sh); - } - // Display the game window ShowWindow(g_platform.hwnd, SW_SHOW); } void Platform::shutdown() { + if (g_platform.xinput.dll) + FreeLibrary(g_platform.xinput.dll); + + if (g_platform.gl.dll) + FreeLibrary(g_platform.gl.dll); + DestroyWindow(g_platform.hwnd); } @@ -334,6 +388,17 @@ LRESULT CALLBACK Blah::win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam PostQuitMessage(0); return 0; + + // Controller connected event + case WM_DEVICECHANGE: + { + // DBT_DEVNODES_CHANGED = 0x0007 + // https://docs.microsoft.com/en-us/windows/win32/devio/wm-devicechange + if (wParam == 0x0007) + win32_detect_joysticks(); + return 0; + } + // Mouse Input case WM_LBUTTONDOWN: g_platform.input_state->mouse.on_press(MouseButton::Left); @@ -410,8 +475,13 @@ LRESULT CALLBACK Blah::win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam void Platform::update(InputState& state) { // store reference to input state + bool first_update = g_platform.input_state == nullptr; g_platform.input_state = &state; + // if this is the first update, poll joysticks that are already connected + if (first_update) + win32_detect_joysticks(); + // Catch & Dispatch Window Messages MSG msg; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) @@ -429,7 +499,7 @@ void Platform::sleep(int milliseconds) void Platform::present() { - if (App::renderer() == Renderer::OpenGL) + if (App::renderer().type == RendererType::OpenGL) { HDC hdc = GetDC(g_platform.hwnd); SwapBuffers(hdc); @@ -463,6 +533,12 @@ void Platform::set_position(int x, int y) SetWindowPos(g_platform.hwnd, NULL, x, y, w, h, 0); } +bool Platform::get_focused() +{ + Log::warn("App::focused not implemented for Win32 yet"); + return true; +} + void Platform::set_fullscreen(bool enabled) { if (g_platform.fullscreen == enabled) @@ -628,30 +704,19 @@ FileRef Platform::file_open(const char* path, FileMode mode) return FileRef(new Win32File(result)); } -// clipboard -void Platform::set_clipboard(const char* text) -{ - BLAH_ASSERT(false, "Not Implemented Yet"); -} - -const char* Platform::get_clipboard() -{ - BLAH_ASSERT(false, "Not Implemented Yet"); -} - void* Platform::gl_get_func(const char* name) { // this check is taken from https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions // wglGetProcAddress doesn't always return valid pointers for some specific methods? - void* p = (void*)g_platform.wglGetProcAddress(name); + void* p = (void*)g_platform.gl.get_proc_address(name); if ((p == 0) || (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) || (p == (void*)-1)) { - p = (void*)GetProcAddress(g_platform.opengl_dll, name); + p = (void*)GetProcAddress(g_platform.gl.dll, name); } return p; @@ -660,7 +725,7 @@ void* Platform::gl_get_func(const char* name) void* Platform::gl_context_create() { HDC hdc = GetDC(g_platform.hwnd); - return g_platform.wglCreateContext(hdc); + return g_platform.gl.create_context(hdc); } void Platform::gl_context_make_current(void* context) @@ -668,15 +733,15 @@ void Platform::gl_context_make_current(void* context) if (context != nullptr) { HDC hdc = GetDC(g_platform.hwnd); - g_platform.wglMakeCurrent(hdc, (HGLRC)context); + g_platform.gl.make_current(hdc, (HGLRC)context); } else - g_platform.wglMakeCurrent(NULL, NULL); + g_platform.gl.make_current(NULL, NULL); } void Platform::gl_context_destroy(void* context) { - g_platform.wglDeleteContext((HGLRC)context); + g_platform.gl.delete_context((HGLRC)context); } void* Platform::d3d11_get_hwnd() @@ -864,4 +929,116 @@ Key Blah::win32_scancode_to_key(WPARAM wParam, LPARAM lParam) return Key::Unknown; } +void Blah::win32_detect_joysticks() +{ + // mark all joysticks as unnacounted for + for (int i = 0; i < Input::max_controllers; i++) + g_platform.joysticks[i].accounted = false; + + // check for xinput controllers + if (g_platform.xinput.dll) + { + for (DWORD index = 0; index < XUSER_MAX_COUNT; index++) + { + // can't get capabilities; not connected + XINPUT_CAPABILITIES xic; + if (g_platform.xinput.get_capabilities(index, 0, &xic) != ERROR_SUCCESS) + continue; + + // already connected + bool already_connected = false; + for (int i = 0; i < Input::max_controllers; i++) + { + auto& it = g_platform.joysticks[i]; + if (it.connected && it.dinstance == GUID_NULL && it.xindex == index) + { + it.accounted = true; + already_connected = true; + break; + } + } + + if (already_connected) + continue; + + // find an empty slot and mark connected + for (int i = 0; i < Input::max_controllers; i++) + { + auto& it = g_platform.joysticks[i]; + if (!it.connected) + { + it.connected = it.accounted = true; + it.dinstance = GUID_NULL; + it.xindex = index; + + Log::info("Connected XInput [%i]", i); + + // TODO: + // Get Product Info & Proper Name + g_platform.input_state->controllers[i].on_connect("Xbox Controller", true, 15, 6, 0, 0, 0); + + break; + } + } + } + } + + // call disconnect on joysticks that aren't accounted for + for (int i = 0; i < Input::max_controllers; i++) + { + auto& it = g_platform.joysticks[i]; + if (it.connected && !it.accounted) + { + Log::info("Disconnected [%i]", i); + g_platform.input_state->controllers[i].on_disconnect(); + it = Win32Platform::Joystick(); + } + } +} + +void Platform::set_clipboard(const char* text) +{ + auto len = strlen(text); + if (auto glob = GlobalAlloc(GMEM_MOVEABLE, len)) + { + if (auto data = GlobalLock(glob)) + { + memcpy(data, text, len); + GlobalUnlock(glob); + + if (OpenClipboard(nullptr)) + { + SetClipboardData(CF_TEXT, data); + CloseClipboard(); + } + } + + GlobalFree(glob); + } +} + +const char* Platform::get_clipboard() +{ + if (OpenClipboard(nullptr)) + { + HANDLE data = GetClipboardData(CF_TEXT); + if (data) + { + auto text = static_cast(GlobalLock(data)); + if (text) + g_platform.clipboard = text; + GlobalUnlock(data); + } + CloseClipboard(); + } + + return g_platform.clipboard.cstr(); +} + +void Platform::open_url(const char* url) +{ + auto cmd = String("start ") + url; + system(cmd.cstr()); +} + #endif // BLAH_PLATFORM_WIN32 diff --git a/src/internal/renderer.h b/src/internal/renderer.h new file mode 100644 index 0000000..dde7524 --- /dev/null +++ b/src/internal/renderer.h @@ -0,0 +1,90 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +#define BLAH_ASSERT_RENDERER() BLAH_ASSERT(Renderer::instance, "Renderer has not been created") + +namespace Blah +{ + class Renderer + { + public: + + // Reference to the current Renderer in use + inline static Renderer* instance = nullptr; + + // Renderer Features + RendererFeatures features; + + virtual ~Renderer() = default; + + // Initialize the Graphics + virtual bool init() = 0; + + // Shutdown the Graphics + virtual void shutdown() = 0; + + // Called once per frame + virtual void update() = 0; + + // Called before rendering begins + virtual void before_render() = 0; + + // Called after renderings ends + virtual void after_render() = 0; + + // Performs a draw call + virtual void render(const RenderPass& pass) = 0; + + // Clears the backbuffer + virtual void clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) = 0; + + // Creates a new Texture. + // if the Texture is invalid, this should return an empty reference. + virtual TextureRef create_texture(int width, int height, TextureFormat format) = 0; + + // Creates a new Target. + // if the Target is invalid, this should return an empty reference. + virtual TargetRef create_target(int width, int height, const TextureFormat* attachments, int attachment_count) = 0; + + // Creates a new Shader. + // if the Shader is invalid, this should return an empty reference. + virtual ShaderRef create_shader(const ShaderData* data) = 0; + + // Creates a new Mesh. + // if the Mesh is invalid, this should return an empty reference. + virtual MeshRef create_mesh() = 0; + + private: + static Renderer* try_make_opengl(); + static Renderer* try_make_d3d11(); + + public: + static Renderer* try_make_renderer(RendererType type) + { + switch (type) + { + case RendererType::None: return nullptr; + case RendererType::OpenGL: return try_make_opengl(); + case RendererType::D3D11: return try_make_d3d11(); + } + + return nullptr; + } + + static RendererType default_type() + { +#if defined(BLAH_RENDERER_D3D11) and defined(_WIN32) + return RendererType::D3D11; +#else + return RendererType::OpenGL; +#endif + } + }; +} \ No newline at end of file diff --git a/src/internal/graphics_d3d11.cpp b/src/internal/renderer_d3d11.cpp similarity index 86% rename from src/internal/graphics_d3d11.cpp rename to src/internal/renderer_d3d11.cpp index 631a1f4..7bebbc1 100644 --- a/src/internal/graphics_d3d11.cpp +++ b/src/internal/renderer_d3d11.cpp @@ -1,9 +1,9 @@ -#ifdef BLAH_GRAPHICS_D3D11 +#ifdef BLAH_RENDERER_D3D11 // TODO: // Note the D3D11 Implementation is still a work-in-progress -#include "graphics.h" +#include "renderer.h" #include "platform.h" #include #include @@ -15,12 +15,16 @@ #include #include +// shorthand to our internal state +#define renderer ((Renderer_D3D11*)Renderer::instance) + namespace Blah { class D3D11_Shader; - struct D3D11 + class Renderer_D3D11 : public Renderer { + public: // main resources ID3D11Device* device = nullptr; ID3D11DeviceContext* context = nullptr; @@ -28,9 +32,6 @@ namespace Blah ID3D11RenderTargetView* backbuffer_view = nullptr; ID3D11DepthStencilView* backbuffer_depth_view = nullptr; - // supported renderer features - RendererFeatures features; - // last backbuffer size Point last_size; @@ -72,6 +73,18 @@ namespace Blah Vector sampler_cache; Vector depthstencil_cache; + bool init() override; + void shutdown() override; + void update() override; + void before_render() override; + void after_render() override; + void render(const RenderPass& pass) override; + void clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) override; + TextureRef create_texture(int width, int height, TextureFormat format) override; + TargetRef create_target(int width, int height, const TextureFormat* attachments, int attachment_count) override; + ShaderRef create_shader(const ShaderData* data) override; + MeshRef create_mesh() override; + ID3D11InputLayout* get_layout(D3D11_Shader* shader, const VertexFormat& format); ID3D11BlendState* get_blend(const BlendMode& blend); ID3D11RasterizerState* get_rasterizer(const RenderPass& pass); @@ -79,17 +92,12 @@ namespace Blah ID3D11DepthStencilState* get_depthstencil(const RenderPass& pass); }; - // D3D11 State - D3D11 state; - // Utility Methods D3D11_BLEND_OP blend_op(BlendOp op); D3D11_BLEND blend_factor(BlendFactor factor); bool reflect_uniforms(Vector& append_uniforms_to, Vector& append_buffers_to, ID3DBlob* shader, ShaderType shader_type); void apply_uniforms(D3D11_Shader* shader, const MaterialRef& material, ShaderType type); - // ~ BEGIN IMPLEMENTATION ~ - class D3D11_Texture : public Texture { private: @@ -160,7 +168,7 @@ namespace Blah m_dxgi_format = desc.Format; - auto hr = state.device->CreateTexture2D(&desc, NULL, &texture); + auto hr = renderer->device->CreateTexture2D(&desc, NULL, &texture); if (!SUCCEEDED(hr)) { if (texture) @@ -171,7 +179,7 @@ namespace Blah if (!is_depth_stencil) { - hr = state.device->CreateShaderResourceView(texture, NULL, &view); + hr = renderer->device->CreateShaderResourceView(texture, NULL, &view); if (!SUCCEEDED(hr)) { texture->Release(); @@ -220,7 +228,7 @@ namespace Blah box.back = 1; // set data - state.context->UpdateSubresource( + renderer->context->UpdateSubresource( texture, 0, &box, @@ -249,7 +257,7 @@ namespace Blah desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.MiscFlags = 0; - hr = state.device->CreateTexture2D(&desc, NULL, &staging); + hr = renderer->device->CreateTexture2D(&desc, NULL, &staging); if (!SUCCEEDED(hr)) { BLAH_ASSERT(false, "Failed to create staging texture to get data"); @@ -266,7 +274,7 @@ namespace Blah box.back = 1; // copy data to staging texture - state.context->CopySubresourceRegion( + renderer->context->CopySubresourceRegion( staging, 0, 0, 0, 0, texture, 0, @@ -274,7 +282,7 @@ namespace Blah // get data D3D11_MAPPED_SUBRESOURCE map; - hr = state.context->Map(staging, 0, D3D11_MAP_READ, 0, &map); + hr = renderer->context->Map(staging, 0, D3D11_MAP_READ, 0, &map); if (!SUCCEEDED(hr)) { @@ -287,7 +295,7 @@ namespace Blah for (int y = 0; y < m_height; y++) memcpy(data + y * bytes_per_row, (unsigned char*)map.pData + map.RowPitch * y, bytes_per_row); - state.context->Unmap(staging, 0); + renderer->context->Unmap(staging, 0); } bool is_framebuffer() const override @@ -316,12 +324,12 @@ namespace Blah if (attachments[i] == TextureFormat::DepthStencil) { - state.device->CreateDepthStencilView(tex->texture, nullptr, &depth_view); + renderer->device->CreateDepthStencilView(tex->texture, nullptr, &depth_view); } else { ID3D11RenderTargetView* view = nullptr; - state.device->CreateRenderTargetView(tex->texture, nullptr, &view); + renderer->device->CreateRenderTargetView(tex->texture, nullptr, &view); color_views.push_back(view); } } @@ -355,7 +363,7 @@ namespace Blah if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color) { for (int i = 0; i < color_views.size(); i++) - state.context->ClearRenderTargetView(color_views[i], col); + renderer->context->ClearRenderTargetView(color_views[i], col); } if (depth_view) @@ -367,7 +375,7 @@ namespace Blah flags |= D3D11_CLEAR_STENCIL; if (flags != 0) - state.context->ClearDepthStencilView(depth_view, flags, depth, stencil); + renderer->context->ClearDepthStencilView(depth_view, flags, depth, stencil); } } }; @@ -442,7 +450,7 @@ namespace Blah // create vertex shader { - hr = state.device->CreateVertexShader( + hr = renderer->device->CreateVertexShader( vertex_blob->GetBufferPointer(), vertex_blob->GetBufferSize(), NULL, @@ -454,7 +462,7 @@ namespace Blah // create fragment shader { - hr = state.device->CreatePixelShader( + hr = renderer->device->CreatePixelShader( fragment_blob->GetBufferPointer(), fragment_blob->GetBufferSize(), NULL, @@ -607,7 +615,7 @@ namespace Blah data.pSysMem = indices; // create - auto hr = state.device->CreateBuffer(&desc, &data, &index_buffer); + auto hr = renderer->device->CreateBuffer(&desc, &data, &index_buffer); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Index Data"); } } @@ -615,13 +623,13 @@ namespace Blah { D3D11_MAPPED_SUBRESOURCE map; - auto hr = state.context->Map(index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + auto hr = renderer->context->Map(index_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Index Data"); if (SUCCEEDED(hr)) { memcpy(map.pData, indices, index_stride * count); - state.context->Unmap(index_buffer, 0); + renderer->context->Unmap(index_buffer, 0); } } } @@ -655,7 +663,7 @@ namespace Blah data.pSysMem = vertices; // create - auto hr = state.device->CreateBuffer(&desc, &data, &vertex_buffer); + auto hr = renderer->device->CreateBuffer(&desc, &data, &vertex_buffer); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Vertex Data"); } } @@ -663,13 +671,13 @@ namespace Blah else if (vertices) { D3D11_MAPPED_SUBRESOURCE map; - auto hr = state.context->Map(vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + auto hr = renderer->context->Map(vertex_buffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &map); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Vertex Data"); if (SUCCEEDED(hr)) { memcpy(map.pData, vertices, vertex_format.stride * count); - state.context->Unmap(vertex_buffer, 0); + renderer->context->Unmap(vertex_buffer, 0); } } } @@ -695,10 +703,9 @@ namespace Blah } }; - bool Graphics::init() + bool Renderer_D3D11::init() { - state = D3D11(); - state.last_size = Point(App::draw_width(), App::draw_height()); + last_size = Point(App::backbuffer()->width(), App::backbuffer()->height()); // Define Swap Chain DXGI_SWAP_CHAIN_DESC desc = {}; @@ -729,21 +736,21 @@ namespace Blah 0, D3D11_SDK_VERSION, &desc, - &state.swap_chain, - &state.device, + &swap_chain, + &device, &feature_level, - &state.context); + &context); // Exit out if it's not OK - if (!SUCCEEDED(hr) || !state.swap_chain || !state.device || !state.context) + if (!SUCCEEDED(hr) || !swap_chain || !device || !context) return false; // Get the backbuffer ID3D11Texture2D* frame_buffer = nullptr; - state.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer); + swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer); if (frame_buffer) { - state.device->CreateRenderTargetView(frame_buffer, nullptr, &state.backbuffer_view); + device->CreateRenderTargetView(frame_buffer, nullptr, &backbuffer_view); frame_buffer->Release(); } @@ -751,9 +758,10 @@ namespace Blah // create a depth backbuffer // Store Features - state.features.instancing = true; - state.features.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; - state.features.origin_bottom_left = false; + features.type = RendererType::D3D11; + features.instancing = true; + features.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; + features.origin_bottom_left = false; // Print Driver Info { @@ -761,7 +769,7 @@ namespace Blah IDXGIAdapter* dxgi_adapter; DXGI_ADAPTER_DESC adapter_desc; - hr = state.device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgi_device); + hr = device->QueryInterface(__uuidof(IDXGIDevice), (void**)&dxgi_device); if (SUCCEEDED(hr)) { @@ -782,85 +790,72 @@ namespace Blah return true; } - Renderer Graphics::renderer() - { - return Renderer::D3D11; - } - - void Graphics::shutdown() + void Renderer_D3D11::shutdown() { // release cached objects - for (auto& it : state.blend_cache) + for (auto& it : blend_cache) it.state->Release(); - for (auto& it : state.depthstencil_cache) + for (auto& it : depthstencil_cache) it.state->Release(); - for (auto& it : state.layout_cache) + for (auto& it : layout_cache) it.layout->Release(); - for (auto& it : state.rasterizer_cache) + for (auto& it : rasterizer_cache) it.state->Release(); - for (auto& it : state.sampler_cache) + for (auto& it : sampler_cache) it.state->Release(); // release main devices - if (state.backbuffer_view) - state.backbuffer_view->Release(); - if (state.backbuffer_depth_view) - state.backbuffer_depth_view->Release(); - state.swap_chain->Release(); - state.context->ClearState(); - state.context->Flush(); - state.context->Release(); - state.device->Release(); - - // reset state - state = D3D11(); + if (backbuffer_view) + backbuffer_view->Release(); + if (backbuffer_depth_view) + backbuffer_depth_view->Release(); + swap_chain->Release(); + context->ClearState(); + context->Flush(); + context->Release(); + device->Release(); } - const RendererFeatures& Graphics::features() + void Renderer_D3D11::update() { - return state.features; + } - void Graphics::update() - { - - } - - void Graphics::before_render() + void Renderer_D3D11::before_render() { HRESULT hr; - auto next_size = Point(App::draw_width(), App::draw_height()); - if (state.last_size != next_size) + auto next_size = Point(App::backbuffer()->width(), App::backbuffer()->height()); + if (last_size != next_size) { // release old buffer - if (state.backbuffer_view) - state.backbuffer_view->Release(); + if (backbuffer_view) + backbuffer_view->Release(); // perform resize - hr = state.swap_chain->ResizeBuffers(0, next_size.x, next_size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0); + hr = swap_chain->ResizeBuffers(0, next_size.x, next_size.y, DXGI_FORMAT_B8G8R8A8_UNORM, 0); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Backbuffer on Resize"); - state.last_size = next_size; + last_size = next_size; // get the new buffer ID3D11Texture2D* frame_buffer = nullptr; - hr = state.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer); + hr = swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&frame_buffer); if (SUCCEEDED(hr) && frame_buffer) { - hr = state.device->CreateRenderTargetView(frame_buffer, nullptr, &state.backbuffer_view); + hr = device->CreateRenderTargetView(frame_buffer, nullptr, &backbuffer_view); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Backbuffer on Resize"); frame_buffer->Release(); } } } - void Graphics::after_render() + void Renderer_D3D11::after_render() { - auto hr = state.swap_chain->Present(1, 0); + auto hr = swap_chain->Present(1, 0); BLAH_ASSERT(SUCCEEDED(hr), "Failed to Present swap chain"); } - TextureRef Graphics::create_texture(int width, int height, TextureFormat format) + TextureRef Renderer_D3D11::create_texture(int width, int height, TextureFormat format) { auto result = new D3D11_Texture(width, height, format, false); @@ -871,12 +866,12 @@ namespace Blah return TextureRef(); } - TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachment_count) + TargetRef Renderer_D3D11::create_target(int width, int height, const TextureFormat* attachments, int attachment_count) { return TargetRef(new D3D11_Target(width, height, attachments, attachment_count)); } - ShaderRef Graphics::create_shader(const ShaderData* data) + ShaderRef Renderer_D3D11::create_shader(const ShaderData* data) { auto result = new D3D11_Shader(data); if (result->valid) @@ -886,23 +881,23 @@ namespace Blah return ShaderRef(); } - MeshRef Graphics::create_mesh() + MeshRef Renderer_D3D11::create_mesh() { return MeshRef(new D3D11_Mesh()); } - void Graphics::render(const RenderPass& pass) + void Renderer_D3D11::render(const RenderPass& pass) { - auto ctx = state.context; + auto ctx = context; auto mesh = (D3D11_Mesh*)pass.mesh.get(); auto shader = (D3D11_Shader*)(pass.material->shader().get()); // OM { // Set the Target - if (pass.target == App::backbuffer || !pass.target) + if (pass.target == App::backbuffer() || !pass.target) { - ctx->OMSetRenderTargets(1, &state.backbuffer_view, state.backbuffer_depth_view); + ctx->OMSetRenderTargets(1, &backbuffer_view, backbuffer_depth_view); } else { @@ -912,14 +907,14 @@ namespace Blah // Depth { - auto depthstencil = state.get_depthstencil(pass); + auto depthstencil = get_depthstencil(pass); if (depthstencil) ctx->OMSetDepthStencilState(depthstencil, 0); } // Blend Mode { - auto blend = state.get_blend(pass.blend); + auto blend = get_blend(pass.blend); if (blend) { auto color = Color::from_rgba(pass.blend.rgba); @@ -941,7 +936,7 @@ namespace Blah ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // Assign Layout - auto layout = state.get_layout(shader, mesh->vertex_format); + auto layout = get_layout(shader, mesh->vertex_format); ctx->IASetInputLayout(layout); // Assign Vertex Buffer @@ -1000,7 +995,7 @@ namespace Blah auto& samplers = pass.material->samplers(); for (int i = 0; i < samplers.size(); i++) { - auto sampler = state.get_sampler(samplers[i]); + auto sampler = get_sampler(samplers[i]); if (sampler) ctx->PSSetSamplers(i, 1, &sampler); } @@ -1034,7 +1029,7 @@ namespace Blah // Rasterizer { - auto rasterizer = state.get_rasterizer(pass); + auto rasterizer = get_rasterizer(pass); if (rasterizer) ctx->RSSetState(rasterizer); } @@ -1065,15 +1060,15 @@ namespace Blah } } - void Graphics::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) + void Renderer_D3D11::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) { if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color) { float clear[4] = { color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f }; - state.context->ClearRenderTargetView(state.backbuffer_view, clear); + context->ClearRenderTargetView(backbuffer_view, clear); } - if (state.backbuffer_depth_view) + if (backbuffer_depth_view) { UINT flags = 0; if (((int)mask & (int)ClearMask::Depth) == (int)ClearMask::Depth) @@ -1082,7 +1077,7 @@ namespace Blah flags |= D3D11_CLEAR_STENCIL; if (flags != 0) - state.context->ClearDepthStencilView(state.backbuffer_depth_view, flags, depth, stencil); + context->ClearDepthStencilView(backbuffer_depth_view, flags, depth, stencil); } } @@ -1179,7 +1174,7 @@ namespace Blah buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; ID3D11Buffer* buffer; - state.device->CreateBuffer(&buffer_desc, nullptr, &buffer); + renderer->device->CreateBuffer(&buffer_desc, nullptr, &buffer); append_buffers_to.push_back(buffer); } @@ -1278,14 +1273,14 @@ namespace Blah if (buffers[i]) { D3D11_MAPPED_SUBRESOURCE map; - state.context->Map(buffers[i], 0, D3D11_MAP_WRITE_DISCARD, 0, &map); + renderer->context->Map(buffers[i], 0, D3D11_MAP_WRITE_DISCARD, 0, &map); memcpy(map.pData, values[i].begin(), values[i].size() * sizeof(float)); - state.context->Unmap(buffers[i], 0); + renderer->context->Unmap(buffers[i], 0); } } } - ID3D11InputLayout* D3D11::get_layout(D3D11_Shader* shader, const VertexFormat& format) + ID3D11InputLayout* Renderer_D3D11::get_layout(D3D11_Shader* shader, const VertexFormat& format) { // find existing for (auto& it : layout_cache) @@ -1374,7 +1369,7 @@ namespace Blah return nullptr; } - ID3D11BlendState* D3D11::get_blend(const BlendMode& blend) + ID3D11BlendState* Renderer_D3D11::get_blend(const BlendMode& blend) { for (auto& it : blend_cache) if (it.blend == blend) @@ -1414,7 +1409,7 @@ namespace Blah desc.RenderTarget[i] = desc.RenderTarget[0]; ID3D11BlendState* blend_state = nullptr; - auto hr = state.device->CreateBlendState(&desc, &blend_state); + auto hr = renderer->device->CreateBlendState(&desc, &blend_state); if (SUCCEEDED(hr)) { @@ -1427,7 +1422,7 @@ namespace Blah return nullptr; } - ID3D11SamplerState* D3D11::get_sampler(const TextureSampler& sampler) + ID3D11SamplerState* Renderer_D3D11::get_sampler(const TextureSampler& sampler) { for (auto& it : sampler_cache) if (it.sampler == sampler) @@ -1462,7 +1457,7 @@ namespace Blah } ID3D11SamplerState* result; - auto hr = state.device->CreateSamplerState(&desc, &result); + auto hr = renderer->device->CreateSamplerState(&desc, &result); if (SUCCEEDED(hr)) { @@ -1475,7 +1470,7 @@ namespace Blah return nullptr; } - ID3D11RasterizerState* D3D11::get_rasterizer(const RenderPass& pass) + ID3D11RasterizerState* Renderer_D3D11::get_rasterizer(const RenderPass& pass) { for (auto& it : rasterizer_cache) if (it.cull == pass.cull && it.has_scissor == pass.has_scissor) @@ -1502,7 +1497,7 @@ namespace Blah desc.AntialiasedLineEnable = false; ID3D11RasterizerState* result; - auto hr = state.device->CreateRasterizerState(&desc, &result); + auto hr = renderer->device->CreateRasterizerState(&desc, &result); if (SUCCEEDED(hr)) { @@ -1516,7 +1511,7 @@ namespace Blah return nullptr; } - ID3D11DepthStencilState* D3D11::get_depthstencil(const RenderPass& pass) + ID3D11DepthStencilState* Renderer_D3D11::get_depthstencil(const RenderPass& pass) { for (auto& it : depthstencil_cache) if (it.depth == pass.depth) @@ -1541,7 +1536,7 @@ namespace Blah } ID3D11DepthStencilState* result; - auto hr = state.device->CreateDepthStencilState(&desc, &result); + auto hr = renderer->device->CreateDepthStencilState(&desc, &result); if (SUCCEEDED(hr)) { @@ -1555,4 +1550,17 @@ namespace Blah } } -#endif // BLAH_GRAPHICS_D3D11 +Blah::Renderer* Blah::Renderer::try_make_d3d11() +{ + return new Blah::Renderer_D3D11(); +} + +#else // BLAH_RENDERER_D3D11 + +#include "renderer.h" +Blah::Renderer* Blah::Renderer::try_make_d3d11() +{ + return nullptr; +} + +#endif diff --git a/src/internal/graphics_gl.cpp b/src/internal/renderer_opengl.cpp similarity index 77% rename from src/internal/graphics_gl.cpp rename to src/internal/renderer_opengl.cpp index a24c119..1390eab 100644 --- a/src/internal/graphics_gl.cpp +++ b/src/internal/renderer_opengl.cpp @@ -1,6 +1,6 @@ -#ifdef BLAH_GRAPHICS_OPENGL +#ifdef BLAH_RENDERER_OPENGL -#include "graphics.h" +#include "renderer.h" #include "platform.h" #include #include @@ -338,14 +338,22 @@ typedef void (APIENTRY* DEBUGPROC)(GLenum source, const GLchar* message, const void* userParam); +// shorthand to our internal state +#define renderer ((Renderer_OpenGL*)Renderer::instance) + namespace Blah { - struct State + class Renderer_OpenGL : public Renderer { - // GL function pointers - #define GL_FUNC(name, ret, ...) typedef ret (*name ## Func) (__VA_ARGS__); name ## Func name; - GL_FUNCTIONS - #undef GL_FUNC + public: + + struct Bindings + { + // GL function pointers + #define GL_FUNC(name, ret, ...) typedef ret (*name ## Func) (__VA_ARGS__); name ## Func name; + GL_FUNCTIONS + #undef GL_FUNC + } gl; // state void* context; @@ -358,11 +366,19 @@ namespace Blah int max_samples; int max_texture_image_units; int max_texture_size; - RendererFeatures features; - }; - // static state - State gl; + bool init() override; + void shutdown() override; + void update() override; + void before_render() override; + void after_render() override; + void render(const RenderPass& pass) override; + void clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) override; + TextureRef create_texture(int width, int height, TextureFormat format) override; + TargetRef create_target(int width, int height, const TextureFormat* attachments, int attachment_count) override; + ShaderRef create_shader(const ShaderData* data) override; + MeshRef create_mesh() override; + }; // debug callback void APIENTRY gl_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) @@ -408,7 +424,7 @@ namespace Blah GLuint gl_mesh_assign_attributes(GLuint buffer, GLenum buffer_type, const VertexFormat& format, GLint divisor) { // bind - gl.BindBuffer(buffer_type, buffer); + renderer->gl.BindBuffer(buffer_type, buffer); // TODO: disable existing enabled attributes .. // ... @@ -485,9 +501,9 @@ namespace Blah } u32 location = (u32)(attribute.index); - gl.EnableVertexAttribArray(location); - gl.VertexAttribPointer(location, components, type, attribute.normalized, format.stride, (void*)ptr); - gl.VertexAttribDivisor(location, divisor); + renderer->gl.EnableVertexAttribArray(location); + renderer->gl.VertexAttribPointer(location, components, type, attribute.normalized, format.stride, (void*)ptr); + renderer->gl.VertexAttribDivisor(location, divisor); ptr += components * component_size; } @@ -565,9 +581,9 @@ namespace Blah m_gl_format = GL_RED; m_gl_type = GL_UNSIGNED_BYTE; - if (width > gl.max_texture_size || height > gl.max_texture_size) + if (width > renderer->max_texture_size || height > renderer->max_texture_size) { - Log::error("Exceeded Max Texture Size of %i", gl.max_texture_size); + Log::error("Exceeded Max Texture Size of %i", renderer->max_texture_size); return; } @@ -601,16 +617,16 @@ namespace Blah return; } - gl.GenTextures(1, &m_id); - gl.ActiveTexture(GL_TEXTURE0); - gl.BindTexture(GL_TEXTURE_2D, m_id); - gl.TexImage2D(GL_TEXTURE_2D, 0, m_gl_internal_format, width, height, 0, m_gl_format, m_gl_type, nullptr); + renderer->gl.GenTextures(1, &m_id); + renderer->gl.ActiveTexture(GL_TEXTURE0); + renderer->gl.BindTexture(GL_TEXTURE_2D, m_id); + renderer->gl.TexImage2D(GL_TEXTURE_2D, 0, m_gl_internal_format, width, height, 0, m_gl_format, m_gl_type, nullptr); } ~OpenGL_Texture() { - if (m_id > 0) - gl.DeleteTextures(1, &m_id); + if (m_id > 0 && renderer) + renderer->gl.DeleteTextures(1, &m_id); } GLuint gl_id() const @@ -639,26 +655,26 @@ namespace Blah { m_sampler = sampler; - gl.BindTexture(GL_TEXTURE_2D, m_id); - gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (m_sampler.filter == TextureFilter::Nearest ? GL_NEAREST : GL_LINEAR)); - gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (m_sampler.filter == TextureFilter::Nearest ? GL_NEAREST : GL_LINEAR)); - gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (m_sampler.wrap_x == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT)); - gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (m_sampler.wrap_y == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT)); + renderer->gl.BindTexture(GL_TEXTURE_2D, m_id); + renderer->gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (m_sampler.filter == TextureFilter::Nearest ? GL_NEAREST : GL_LINEAR)); + renderer->gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (m_sampler.filter == TextureFilter::Nearest ? GL_NEAREST : GL_LINEAR)); + renderer->gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (m_sampler.wrap_x == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT)); + renderer->gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (m_sampler.wrap_y == TextureWrap::Clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT)); } } virtual void set_data(unsigned char* data) override { - gl.ActiveTexture(GL_TEXTURE0); - gl.BindTexture(GL_TEXTURE_2D, m_id); - gl.TexImage2D(GL_TEXTURE_2D, 0, m_gl_internal_format, m_width, m_height, 0, m_gl_format, m_gl_type, data); + renderer->gl.ActiveTexture(GL_TEXTURE0); + renderer->gl.BindTexture(GL_TEXTURE_2D, m_id); + renderer->gl.TexImage2D(GL_TEXTURE_2D, 0, m_gl_internal_format, m_width, m_height, 0, m_gl_format, m_gl_type, data); } virtual void get_data(unsigned char* data) override { - gl.ActiveTexture(GL_TEXTURE0); - gl.BindTexture(GL_TEXTURE_2D, m_id); - gl.GetTexImage(GL_TEXTURE_2D, 0, m_gl_internal_format, m_gl_type, data); + renderer->gl.ActiveTexture(GL_TEXTURE0); + renderer->gl.BindTexture(GL_TEXTURE_2D, m_id); + renderer->gl.GetTexImage(GL_TEXTURE_2D, 0, m_gl_internal_format, m_gl_type, data); } virtual bool is_framebuffer() const override @@ -680,11 +696,11 @@ namespace Blah OpenGL_Target(int width, int height, const TextureFormat* attachments, int attachmentCount) { - gl.GenFramebuffers(1, &m_id); + renderer->gl.GenFramebuffers(1, &m_id); m_width = width; m_height = height; - gl.BindFramebuffer(GL_FRAMEBUFFER, m_id); + renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, m_id); for (int i = 0; i < attachmentCount; i++) { @@ -696,20 +712,20 @@ namespace Blah if (attachments[i] != TextureFormat::DepthStencil) { - gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, gltex->gl_id(), 0); + renderer->gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, gltex->gl_id(), 0); } else { - gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, gltex->gl_id(), 0); + renderer->gl.FramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, gltex->gl_id(), 0); } } } ~OpenGL_Target() { - if (m_id > 0) + if (m_id > 0 && renderer) { - gl.DeleteFramebuffers(1, &m_id); + renderer->gl.DeleteFramebuffers(1, &m_id); m_id = 0; } } @@ -740,13 +756,13 @@ namespace Blah if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil) clear |= GL_STENCIL_BUFFER_BIT; - gl.BindFramebuffer(GL_FRAMEBUFFER, m_id); - gl.Disable(GL_SCISSOR_TEST); - gl.ColorMask(true, true, true, true); - gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f); - gl.ClearDepth(depth); - gl.ClearStencil(stencil); - gl.Clear(clear); + renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, m_id); + renderer->gl.Disable(GL_SCISSOR_TEST); + renderer->gl.ColorMask(true, true, true, true); + renderer->gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f); + renderer->gl.ClearDepth(depth); + renderer->gl.ClearStencil(stencil); + renderer->gl.Clear(clear); } }; @@ -778,47 +794,47 @@ namespace Blah GLchar log[1024]; GLsizei log_length = 0; - GLuint vertex_shader = gl.CreateShader(GL_VERTEX_SHADER); + GLuint vertex_shader = renderer->gl.CreateShader(GL_VERTEX_SHADER); { const GLchar* source = (const GLchar*)data->vertex.cstr(); - gl.ShaderSource(vertex_shader, 1, &source, nullptr); - gl.CompileShader(vertex_shader); - gl.GetShaderInfoLog(vertex_shader, 1024, &log_length, log); + renderer->gl.ShaderSource(vertex_shader, 1, &source, nullptr); + renderer->gl.CompileShader(vertex_shader); + renderer->gl.GetShaderInfoLog(vertex_shader, 1024, &log_length, log); if (log_length > 0) { - gl.DeleteShader(vertex_shader); + renderer->gl.DeleteShader(vertex_shader); Log::error(log); return; } } - GLuint fragment_shader = gl.CreateShader(GL_FRAGMENT_SHADER); + GLuint fragment_shader = renderer->gl.CreateShader(GL_FRAGMENT_SHADER); { const GLchar* source = (const GLchar*)data->fragment.cstr(); - gl.ShaderSource(fragment_shader, 1, &source, nullptr); - gl.CompileShader(fragment_shader); - gl.GetShaderInfoLog(fragment_shader, 1024, &log_length, log); + renderer->gl.ShaderSource(fragment_shader, 1, &source, nullptr); + renderer->gl.CompileShader(fragment_shader); + renderer->gl.GetShaderInfoLog(fragment_shader, 1024, &log_length, log); if (log_length > 0) { - gl.DeleteShader(vertex_shader); - gl.DeleteShader(fragment_shader); + renderer->gl.DeleteShader(vertex_shader); + renderer->gl.DeleteShader(fragment_shader); Log::error(log); return; } } // create actual shader program - GLuint id = gl.CreateProgram(); - gl.AttachShader(id, vertex_shader); - gl.AttachShader(id, fragment_shader); - gl.LinkProgram(id); - gl.GetProgramInfoLog(id, 1024, &log_length, log); - gl.DetachShader(id, vertex_shader); - gl.DetachShader(id, fragment_shader); - gl.DeleteShader(vertex_shader); - gl.DeleteShader(fragment_shader); + GLuint id = renderer->gl.CreateProgram(); + renderer->gl.AttachShader(id, vertex_shader); + renderer->gl.AttachShader(id, fragment_shader); + renderer->gl.LinkProgram(id); + renderer->gl.GetProgramInfoLog(id, 1024, &log_length, log); + renderer->gl.DetachShader(id, vertex_shader); + renderer->gl.DetachShader(id, fragment_shader); + renderer->gl.DeleteShader(vertex_shader); + renderer->gl.DeleteShader(fragment_shader); if (log_length > 0) { @@ -832,7 +848,7 @@ namespace Blah const int max_name_length = 256; GLint active_uniforms = 0; - gl.GetProgramiv(id, GL_ACTIVE_UNIFORMS, &active_uniforms); + renderer->gl.GetProgramiv(id, GL_ACTIVE_UNIFORMS, &active_uniforms); for (int i = 0; i < active_uniforms; i++) { @@ -841,7 +857,7 @@ namespace Blah GLenum type; GLchar name[max_name_length + 1]; - gl.GetActiveUniform(id, i, max_name_length, &length, &size, &type, name); + renderer->gl.GetActiveUniform(id, i, max_name_length, &length, &size, &type, name); name[length] = '\0'; // array names end with "[0]", and we don't want that @@ -863,7 +879,7 @@ namespace Blah tex_uniform.array_length = size; tex_uniform.type = UniformType::Texture2D; tex_uniform.shader = ShaderType::Fragment; - uniform_locations.push_back(gl.GetUniformLocation(id, name)); + uniform_locations.push_back(renderer->gl.GetUniformLocation(id, name)); m_uniforms.push_back(tex_uniform); UniformInfo sampler_uniform; @@ -872,7 +888,7 @@ namespace Blah sampler_uniform.array_length = size; sampler_uniform.type = UniformType::Sampler2D; sampler_uniform.shader = ShaderType::Fragment; - uniform_locations.push_back(gl.GetUniformLocation(id, name)); + uniform_locations.push_back(renderer->gl.GetUniformLocation(id, name)); m_uniforms.push_back(sampler_uniform); } else @@ -882,7 +898,7 @@ namespace Blah uniform.type = UniformType::None; uniform.buffer_index = 0; uniform.array_length = size; - uniform_locations.push_back(gl.GetUniformLocation(id, name)); + uniform_locations.push_back(renderer->gl.GetUniformLocation(id, name)); uniform.shader = (ShaderType)((int)ShaderType::Vertex | (int)ShaderType::Fragment); if (type == GL_FLOAT) @@ -912,15 +928,15 @@ namespace Blah // assign ID if the uniforms were valid if (!valid_uniforms) - gl.DeleteProgram(id); + renderer->gl.DeleteProgram(id); else m_id = id; } ~OpenGL_Shader() { - if (m_id > 0) - gl.DeleteProgram(m_id); + if (m_id > 0 && renderer) + renderer->gl.DeleteProgram(m_id); m_id = 0; } @@ -975,19 +991,22 @@ namespace Blah m_vertex_attribs_enabled = 0; m_instance_attribs_enabled = 0; - gl.GenVertexArrays(1, &m_id); + renderer->gl.GenVertexArrays(1, &m_id); } ~OpenGL_Mesh() { - if (m_vertex_buffer != 0) - gl.DeleteBuffers(1, &m_vertex_buffer); - if (m_index_buffer != 0) - gl.DeleteBuffers(1, &m_index_buffer); - if (m_instance_buffer != 0) - gl.DeleteBuffers(1, &m_instance_buffer); - if (m_id != 0) - gl.DeleteVertexArrays(1, &m_id); + if (renderer) + { + if (m_vertex_buffer != 0) + renderer->gl.DeleteBuffers(1, &m_vertex_buffer); + if (m_index_buffer != 0) + renderer->gl.DeleteBuffers(1, &m_index_buffer); + if (m_instance_buffer != 0) + renderer->gl.DeleteBuffers(1, &m_instance_buffer); + if (m_id != 0) + renderer->gl.DeleteVertexArrays(1, &m_id); + } m_id = 0; } @@ -1010,10 +1029,10 @@ namespace Blah { m_index_count = count; - gl.BindVertexArray(m_id); + renderer->gl.BindVertexArray(m_id); { if (m_index_buffer == 0) - gl.GenBuffers(1, &(m_index_buffer)); + renderer->gl.GenBuffers(1, &(m_index_buffer)); switch (format) { @@ -1027,52 +1046,52 @@ namespace Blah break; } - gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer); - gl.BufferData(GL_ELEMENT_ARRAY_BUFFER, m_index_size * count, indices, GL_DYNAMIC_DRAW); + renderer->gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer); + renderer->gl.BufferData(GL_ELEMENT_ARRAY_BUFFER, m_index_size * count, indices, GL_DYNAMIC_DRAW); } - gl.BindVertexArray(0); + renderer->gl.BindVertexArray(0); } virtual void vertex_data(const VertexFormat& format, const void* vertices, i64 count) override { m_vertex_count = count; - gl.BindVertexArray(m_id); + renderer->gl.BindVertexArray(m_id); { // Create Buffer if it doesn't exist yet if (m_vertex_buffer == 0) - gl.GenBuffers(1, &(m_vertex_buffer)); + renderer->gl.GenBuffers(1, &(m_vertex_buffer)); // TODO: // Cache this m_vertex_size = gl_mesh_assign_attributes(m_vertex_buffer, GL_ARRAY_BUFFER, format, 0); // Upload Buffer - gl.BindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); - gl.BufferData(GL_ARRAY_BUFFER, m_vertex_size * count, vertices, GL_DYNAMIC_DRAW); + renderer->gl.BindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); + renderer->gl.BufferData(GL_ARRAY_BUFFER, m_vertex_size * count, vertices, GL_DYNAMIC_DRAW); } - gl.BindVertexArray(0); + renderer->gl.BindVertexArray(0); } virtual void instance_data(const VertexFormat& format, const void* instances, i64 count) override { m_instance_count = count; - gl.BindVertexArray(m_id); + renderer->gl.BindVertexArray(m_id); { // Create Buffer if it doesn't exist yet if (m_instance_buffer == 0) - gl.GenBuffers(1, &(m_instance_buffer)); + renderer->gl.GenBuffers(1, &(m_instance_buffer)); // TODO: // Cache this m_instance_size = gl_mesh_assign_attributes(m_instance_buffer, GL_ARRAY_BUFFER, format, 1); // Upload Buffer - gl.BindBuffer(GL_ARRAY_BUFFER, m_instance_buffer); - gl.BufferData(GL_ARRAY_BUFFER, m_instance_size * count, instances, GL_DYNAMIC_DRAW); + renderer->gl.BindBuffer(GL_ARRAY_BUFFER, m_instance_buffer); + renderer->gl.BufferData(GL_ARRAY_BUFFER, m_instance_size * count, instances, GL_DYNAMIC_DRAW); } - gl.BindVertexArray(0); + renderer->gl.BindVertexArray(0); } virtual i64 index_count() const override @@ -1091,21 +1110,19 @@ namespace Blah } }; - bool Graphics::init() + bool Renderer_OpenGL::init() { - gl = State(); - // create gl context - gl.context = Platform::gl_context_create(); - if (gl.context == nullptr) + context = Platform::gl_context_create(); + if (context == nullptr) { Log::error("Failed to create OpenGL Context"); return false; } - Platform::gl_context_make_current(gl.context); + Platform::gl_context_make_current(context); // bind opengl functions - #define GL_FUNC(name, ...) gl.name = (State::name ## Func)(Platform::gl_get_func("gl" #name)); + #define GL_FUNC(name, ...) gl.name = (Renderer_OpenGL::Bindings::name ## Func)(Platform::gl_get_func("gl" #name)); GL_FUNCTIONS #undef GL_FUNC @@ -1118,13 +1135,13 @@ namespace Blah } // get opengl info - gl.GetIntegerv(0x8CDF, &gl.max_color_attachments); - gl.GetIntegerv(0x80E9, &gl.max_element_indices); - gl.GetIntegerv(0x80E8, &gl.max_element_vertices); - gl.GetIntegerv(0x84E8, &gl.max_renderbuffer_size); - gl.GetIntegerv(0x8D57, &gl.max_samples); - gl.GetIntegerv(0x8872, &gl.max_texture_image_units); - gl.GetIntegerv(0x0D33, &gl.max_texture_size); + gl.GetIntegerv(0x8CDF, &max_color_attachments); + gl.GetIntegerv(0x80E9, &max_element_indices); + gl.GetIntegerv(0x80E8, &max_element_vertices); + gl.GetIntegerv(0x84E8, &max_renderbuffer_size); + gl.GetIntegerv(0x8D57, &max_samples); + gl.GetIntegerv(0x8872, &max_texture_image_units); + gl.GetIntegerv(0x0D33, &max_texture_size); // log Log::info("OpenGL %s, %s", @@ -1136,34 +1153,25 @@ namespace Blah gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1); // assign info - gl.features.instancing = true; - gl.features.origin_bottom_left = true; - gl.features.max_texture_size = gl.max_texture_size; + features.type = RendererType::OpenGL; + features.instancing = true; + features.origin_bottom_left = true; + features.max_texture_size = max_texture_size; return true; } - Renderer Graphics::renderer() + void Renderer_OpenGL::shutdown() { - return Renderer::OpenGL; + Platform::gl_context_destroy(context); + context = nullptr; } - void Graphics::shutdown() - { - Platform::gl_context_destroy(gl.context); - gl.context = nullptr; - } + void Renderer_OpenGL::update() {} + void Renderer_OpenGL::before_render() {} + void Renderer_OpenGL::after_render() {} - const RendererFeatures& Graphics::features() - { - return gl.features; - } - - void Graphics::update() {} - void Graphics::before_render() {} - void Graphics::after_render() {} - - TextureRef Graphics::create_texture(int width, int height, TextureFormat format) + TextureRef Renderer_OpenGL::create_texture(int width, int height, TextureFormat format) { auto resource = new OpenGL_Texture(width, height, format); @@ -1176,7 +1184,7 @@ namespace Blah return TextureRef(resource); } - TargetRef Graphics::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount) + TargetRef Renderer_OpenGL::create_target(int width, int height, const TextureFormat* attachments, int attachmentCount) { auto resource = new OpenGL_Target(width, height, attachments, attachmentCount); @@ -1189,7 +1197,7 @@ namespace Blah return TargetRef(resource); } - ShaderRef Graphics::create_shader(const ShaderData* data) + ShaderRef Renderer_OpenGL::create_shader(const ShaderData* data) { auto resource = new OpenGL_Shader(data); @@ -1202,7 +1210,7 @@ namespace Blah return ShaderRef(resource); } - MeshRef Graphics::create_mesh() + MeshRef Renderer_OpenGL::create_mesh() { auto resource = new OpenGL_Mesh(); @@ -1215,24 +1223,20 @@ namespace Blah return MeshRef(resource); } - void Graphics::render(const RenderPass& pass) + void Renderer_OpenGL::render(const RenderPass& pass) { // Bind the Target - Point size; - if (pass.target == App::backbuffer) + if (pass.target == App::backbuffer()) { - gl.BindFramebuffer(GL_FRAMEBUFFER, 0); - size.x = App::draw_width(); - size.y = App::draw_height(); + renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, 0); } else if (pass.target) { auto framebuffer = (OpenGL_Target*)pass.target.get(); - gl.BindFramebuffer(GL_FRAMEBUFFER, framebuffer->gl_id()); - size.x = pass.target->width(); - size.y = pass.target->height(); + renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, framebuffer->gl_id()); } + auto size = Point(pass.target->width(), pass.target->height()); auto shader_ref = pass.material->shader(); auto shader = (OpenGL_Shader*)shader_ref.get(); auto mesh = (OpenGL_Mesh*)pass.mesh.get(); @@ -1241,7 +1245,7 @@ namespace Blah // TODO: I don't love how material values are assigned or set here // TODO: this should be cached? { - gl.UseProgram(shader->gl_id()); + renderer->gl.UseProgram(shader->gl_id()); int texture_slot = 0; int gl_texture_slot = 0; @@ -1266,24 +1270,24 @@ namespace Blah auto tex = pass.material->get_texture(texture_slot, n); auto sampler = pass.material->get_sampler(texture_slot, n); - gl.ActiveTexture(GL_TEXTURE0 + gl_texture_slot); + renderer->gl.ActiveTexture(GL_TEXTURE0 + gl_texture_slot); if (!tex) { - gl.BindTexture(GL_TEXTURE_2D, 0); + renderer->gl.BindTexture(GL_TEXTURE_2D, 0); } else { auto gl_tex = ((OpenGL_Texture*)tex.get()); gl_tex->update_sampler(sampler); - gl.BindTexture(GL_TEXTURE_2D, gl_tex->gl_id()); + renderer->gl.BindTexture(GL_TEXTURE_2D, gl_tex->gl_id()); } texture_ids[n] = gl_texture_slot; gl_texture_slot++; } - gl.Uniform1iv(location, (GLint)uniform.array_length, &texture_ids[0]); + renderer->gl.Uniform1iv(location, (GLint)uniform.array_length, &texture_ids[0]); texture_slot++; continue; } @@ -1291,37 +1295,37 @@ namespace Blah // Float if (uniform.type == UniformType::Float) { - gl.Uniform1fv(location, (GLint)uniform.array_length, data); + renderer->gl.Uniform1fv(location, (GLint)uniform.array_length, data); data += uniform.array_length; } // Float2 else if (uniform.type == UniformType::Float2) { - gl.Uniform2fv(location, (GLint)uniform.array_length, data); + renderer->gl.Uniform2fv(location, (GLint)uniform.array_length, data); data += 2 * uniform.array_length; } // Float3 else if (uniform.type == UniformType::Float3) { - gl.Uniform3fv(location, (GLint)uniform.array_length, data); + renderer->gl.Uniform3fv(location, (GLint)uniform.array_length, data); data += 3 * uniform.array_length; } // Float4 else if (uniform.type == UniformType::Float4) { - gl.Uniform4fv(location, (GLint)uniform.array_length, data); + renderer->gl.Uniform4fv(location, (GLint)uniform.array_length, data); data += 4 * uniform.array_length; } // Matrix3x2 else if (uniform.type == UniformType::Mat3x2) { - gl.UniformMatrix3x2fv(location, (GLint)uniform.array_length, 0, data); + renderer->gl.UniformMatrix3x2fv(location, (GLint)uniform.array_length, 0, data); data += 6 * uniform.array_length; } // Matrix4x4 else if (uniform.type == UniformType::Mat4x4) { - gl.UniformMatrix4fv(location, (GLint)uniform.array_length, 0, data); + renderer->gl.UniformMatrix4fv(location, (GLint)uniform.array_length, 0, data); data += 16 * uniform.array_length; } } @@ -1336,11 +1340,11 @@ namespace Blah GLenum alphaSrc = gl_get_blend_factor(pass.blend.alpha_src); GLenum alphaDst = gl_get_blend_factor(pass.blend.alpha_dst); - gl.Enable(GL_BLEND); - gl.BlendEquationSeparate(colorOp, alphaOp); - gl.BlendFuncSeparate(colorSrc, colorDst, alphaSrc, alphaDst); + renderer->gl.Enable(GL_BLEND); + renderer->gl.BlendEquationSeparate(colorOp, alphaOp); + renderer->gl.BlendFuncSeparate(colorSrc, colorDst, alphaSrc, alphaDst); - gl.ColorMask( + renderer->gl.ColorMask( ((int)pass.blend.mask & (int)BlendMask::Red), ((int)pass.blend.mask & (int)BlendMask::Green), ((int)pass.blend.mask & (int)BlendMask::Blue), @@ -1351,7 +1355,7 @@ namespace Blah unsigned char b = pass.blend.rgba >> 8; unsigned char a = pass.blend.rgba; - gl.BlendColor( + renderer->gl.BlendColor( r / 255.0f, g / 255.0f, b / 255.0f, @@ -1362,38 +1366,38 @@ namespace Blah { if (pass.depth == Compare::None) { - gl.Disable(GL_DEPTH_TEST); + renderer->gl.Disable(GL_DEPTH_TEST); } else { - gl.Enable(GL_DEPTH_TEST); + renderer->gl.Enable(GL_DEPTH_TEST); switch (pass.depth) { case Compare::None: break; case Compare::Always: - gl.DepthFunc(GL_ALWAYS); + renderer->gl.DepthFunc(GL_ALWAYS); break; case Compare::Equal: - gl.DepthFunc(GL_EQUAL); + renderer->gl.DepthFunc(GL_EQUAL); break; case Compare::Greater: - gl.DepthFunc(GL_GREATER); + renderer->gl.DepthFunc(GL_GREATER); break; case Compare::GreatorOrEqual: - gl.DepthFunc(GL_GEQUAL); + renderer->gl.DepthFunc(GL_GEQUAL); break; case Compare::Less: - gl.DepthFunc(GL_LESS); + renderer->gl.DepthFunc(GL_LESS); break; case Compare::LessOrEqual: - gl.DepthFunc(GL_LEQUAL); + renderer->gl.DepthFunc(GL_LEQUAL); break; case Compare::Never: - gl.DepthFunc(GL_NEVER); + renderer->gl.DepthFunc(GL_NEVER); break; case Compare::NotEqual: - gl.DepthFunc(GL_NOTEQUAL); + renderer->gl.DepthFunc(GL_NOTEQUAL); break; } } @@ -1403,18 +1407,18 @@ namespace Blah { if (pass.cull == Cull::None) { - gl.Disable(GL_CULL_FACE); + renderer->gl.Disable(GL_CULL_FACE); } else { - gl.Enable(GL_CULL_FACE); + renderer->gl.Enable(GL_CULL_FACE); if (pass.cull == Cull::Back) - gl.CullFace(GL_BACK); + renderer->gl.CullFace(GL_BACK); else if (pass.cull == Cull::Front) - gl.CullFace(GL_FRONT); + renderer->gl.CullFace(GL_FRONT); else - gl.CullFace(GL_FRONT_AND_BACK); + renderer->gl.CullFace(GL_FRONT_AND_BACK); } } @@ -1423,14 +1427,14 @@ namespace Blah Rectf viewport = pass.viewport; viewport.y = size.y - viewport.y - viewport.h; - gl.Viewport((GLint)viewport.x, (GLint)viewport.y, (GLint)viewport.w, (GLint)viewport.h); + renderer->gl.Viewport((GLint)viewport.x, (GLint)viewport.y, (GLint)viewport.w, (GLint)viewport.h); } // Scissor { if (!pass.has_scissor) { - gl.Disable(GL_SCISSOR_TEST); + renderer->gl.Disable(GL_SCISSOR_TEST); } else { @@ -1442,21 +1446,21 @@ namespace Blah if (scissor.h < 0) scissor.h = 0; - gl.Enable(GL_SCISSOR_TEST); - gl.Scissor((GLint)scissor.x, (GLint)scissor.y, (GLint)scissor.w, (GLint)scissor.h); + renderer->gl.Enable(GL_SCISSOR_TEST); + renderer->gl.Scissor((GLint)scissor.x, (GLint)scissor.y, (GLint)scissor.w, (GLint)scissor.h); } } // Draw the Mesh { - gl.BindVertexArray(mesh->gl_id()); + renderer->gl.BindVertexArray(mesh->gl_id()); GLenum index_format = mesh->gl_index_format(); int index_size = mesh->gl_index_size(); if (pass.instance_count > 0) { - gl.DrawElementsInstanced( + renderer->gl.DrawElementsInstanced( GL_TRIANGLES, (GLint)(pass.index_count), index_format, @@ -1465,18 +1469,18 @@ namespace Blah } else { - gl.DrawElements( + renderer->gl.DrawElements( GL_TRIANGLES, (GLint)(pass.index_count), index_format, (void*)(index_size * pass.index_start)); } - gl.BindVertexArray(0); + renderer->gl.BindVertexArray(0); } } - void Graphics::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) + void Renderer_OpenGL::clear_backbuffer(Color color, float depth, u8 stencil, ClearMask mask) { int clear = 0; @@ -1487,14 +1491,28 @@ namespace Blah if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil) clear |= GL_STENCIL_BUFFER_BIT; - gl.BindFramebuffer(GL_FRAMEBUFFER, 0); - gl.Disable(GL_SCISSOR_TEST); - gl.ColorMask(true, true, true, true); - gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f); - gl.ClearDepth(depth); - gl.ClearStencil(stencil); - gl.Clear(clear); + renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, 0); + renderer->gl.Disable(GL_SCISSOR_TEST); + renderer->gl.ColorMask(true, true, true, true); + renderer->gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f); + renderer->gl.ClearDepth(depth); + renderer->gl.ClearStencil(stencil); + renderer->gl.Clear(clear); } } -#endif // BLAH_GRAPHICS_OPENGL +Blah::Renderer* Blah::Renderer::try_make_opengl() +{ + return new Blah::Renderer_OpenGL(); +} + +#else // BLAH_RENDERER_OPENGL + +#include "renderer.h" +Blah::Renderer* Blah::Renderer::try_make_opengl() +{ + return nullptr; +} + +#endif +