Refactored Graphics to allow Renderer choice at runtime

This commit is contained in:
Noel Berry 2022-02-09 18:49:47 -08:00
parent ae6a2f12d4
commit f5e8de0b11
20 changed files with 1121 additions and 1099 deletions

View File

@ -38,9 +38,8 @@ add_library(blah
src/streams/memorystream.cpp src/streams/memorystream.cpp
src/streams/stream.cpp src/streams/stream.cpp
src/internal/graphics_gl.cpp src/internal/renderer_opengl.cpp
src/internal/graphics_d3d11.cpp src/internal/renderer_d3d11.cpp
src/internal/graphics_dummy.cpp
src/internal/platform_sdl2.cpp src/internal/platform_sdl2.cpp
src/internal/platform_win32.cpp src/internal/platform_win32.cpp
) )
@ -55,22 +54,24 @@ target_include_directories(blah
# Platform Variables # Platform Variables
set(BLAH_PLATFORM_SDL2 true CACHE BOOL "Use SDL2 Platform Backend") set(BLAH_PLATFORM_SDL2 true CACHE BOOL "Use SDL2 Platform Backend")
set(BLAH_PLATFORM_WIN32 false CACHE BOOL "Use Win32 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_RENDERER_OPENGL true CACHE BOOL "Make OpenGL Renderer available")
set(BLAH_GRAPHICS_D3D11 false CACHE BOOL "Use D3D11 Graphics Backend") 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 "") set(LIBS "")
# use the OpenGL Graphics Backend # use the OpenGL Renderer Backend
if (BLAH_GRAPHICS_OPENGL) if (BLAH_RENDERER_OPENGL)
add_compile_definitions(BLAH_RENDERER_OPENGL)
endif()
add_compile_definitions(BLAH_GRAPHICS_OPENGL) # use the D3D11 Renderer Backend
if (BLAH_RENDERER_D3D11)
# use the D3D11 Graphics Backend add_compile_definitions(BLAH_RENDERER_D3D11)
elseif (BLAH_GRAPHICS_D3D11)
add_compile_definitions(BLAH_GRAPHICS_D3D11)
set(LIBS ${LIBS} d3d11.lib dxguid.lib D3Dcompiler.lib) set(LIBS ${LIBS} d3d11.lib dxguid.lib D3Dcompiler.lib)
endif() endif()
# use the SDL2 Platform Backend # use the SDL2 Platform Backend
@ -91,7 +92,7 @@ if (BLAH_PLATFORM_SDL2)
FetchContent_Declare( FetchContent_Declare(
SDL2 SDL2
GIT_REPOSITORY https://github.com/libsdl-org/SDL 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 GIT_PROGRESS TRUE
) )
FetchContent_MakeAvailable(SDL2) FetchContent_MakeAvailable(SDL2)

View File

@ -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) - [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` - [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) - 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: - At least one *Renderer* 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) - [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/graphics_d3d11.cpp) can be enabled in CMake with `BLAH_GRAPHICS_D3D11` - [D3D11](https://github.com/NoelFB/blah/blob/master/src/internal/renderer_d3d11.cpp) can be enabled in CMake with `BLAH_RENDERER_D3D11`
- Additional graphics can be added by implementing the [Graphics Backend](https://github.com/NoelFB/blah/blob/master/src/internal/graphics.h) - Additional renderers can be added by implementing the [Renderer Backend](https://github.com/NoelFB/blah/blob/master/src/internal/renderer.h)
#### notes #### 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. - 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,38 +27,26 @@ A small 2D C++ Game Framework, using few dependencies and simple code to mainain
using namespace Blah; using namespace Blah;
Batch batch; 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() int main()
{ {
Config config; Config config;
config.name = "blah app"; config.name = "blah app";
config.width = 1280; config.on_render = []()
config.height = 720; {
config.on_startup = startup; App::backbuffer()->clear(Color::black);
config.on_render = render;
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); App::run(&config);
return 0; return 0;

View File

@ -10,74 +10,20 @@ namespace Blah
// Application Logging Functions // Application Logging Functions
using AppLogFn = Func<void(const char* message, Log::Category category)>; using AppLogFn = Func<void(const char* message, Log::Category category)>;
// Application Configuration // Type of Renderer the Application is using
struct Config enum class RendererType
{
// 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
{ {
None = -1, None = -1,
OpenGL, OpenGL,
D3D11, D3D11,
Metal,
Count
}; };
// Features available on the current Renderer // Renderer Information
struct RendererFeatures struct RendererFeatures
{ {
// The type of Renderer being used
RendererType type = RendererType::None;
// Whether Mesh Instancing is available // Whether Mesh Instancing is available
bool instancing = false; bool instancing = false;
@ -89,6 +35,55 @@ namespace Blah
int max_texture_size = 0; 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 // Forward declare Target for the BackBuffer
class Target; class Target;
using TargetRef = Ref<Target>; using TargetRef = Ref<Target>;
@ -119,31 +114,22 @@ namespace Blah
// Sets the Window Title // Sets the Window Title
void set_title(const char* title); void set_title(const char* title);
// Gets the Window Position // Gets the Window Position in Screen Coordinates
Point get_position(); Point get_position();
// Sets the Window Position // Sets the Window Position in Screen Coordinates
void set_position(Point point); 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(); Point get_size();
// Sets the Window Size // Sets the Window Size in Screen Coordinates
void set_size(Point point); void set_size(Point point);
// Gets the width of the window // Gets the size of the BackBuffer, in pixels
int width(); Point get_backbuffer_size();
// 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 content scale based on the platform. // Gets the content scale based on the platform.
// macOS is usually 2.0, other platforms vary. // macOS is usually 2.0, other platforms vary.
@ -156,14 +142,11 @@ namespace Blah
// Otherwise this function does nothing. // Otherwise this function does nothing.
void fullscreen(bool enabled); void fullscreen(bool enabled);
// Returns the Rendering API in use
Renderer renderer();
// Retrieves the Renderer Features // Retrieves the Renderer Features
const RendererFeatures& renderer_features(); const RendererFeatures& renderer();
// Reference to the window's back buffer // Gets the BackBuffer
extern const TargetRef backbuffer; const TargetRef& backbuffer();
} }
namespace System namespace System

View File

@ -128,7 +128,7 @@ namespace Blah
void set_sampler(const TextureSampler& sampler); void set_sampler(const TextureSampler& sampler);
// Draws the batch to the given target // 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 // Draws the batch to the given target, with the provided matrix
void render(const TargetRef& target, const Mat4x4f& matrix); void render(const TargetRef& target, const Mat4x4f& matrix);

View File

@ -3,7 +3,7 @@
#include <blah/time.h> #include <blah/time.h>
#include <blah/graphics/target.h> #include <blah/graphics/target.h>
#include "internal/platform.h" #include "internal/platform.h"
#include "internal/graphics.h" #include "internal/renderer.h"
#include "internal/input.h" #include "internal/input.h"
#ifdef __EMSCRIPTEN__ #ifdef __EMSCRIPTEN__
@ -13,286 +13,7 @@
using namespace Blah; using namespace Blah;
Config::Config() #define BLAH_ASSERT_RUNNING() BLAH_ASSERT(app_is_running, "The App is not running (call App::run)")
{
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);
}
namespace namespace
{ {
@ -316,20 +37,315 @@ namespace
int width() const override int width() const override
{ {
return App::draw_width(); int w, h;
Platform::get_draw_size(&w, &h);
return w;
} }
int height() const override 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 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()); 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);
}

View File

@ -378,7 +378,7 @@ void Batch::set_texture(const TextureRef& texture)
if (m_batch.texture != texture) if (m_batch.texture != texture)
{ {
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) void Batch::render(const TargetRef& target)
{ {
Point size; TargetRef ref = (target ? target : App::backbuffer());
if (!target) render(ref, Mat4x4f::create_ortho_offcenter(0, (float)ref->width(), (float)ref->height(), 0, 0.01f, 1000.0f));
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));
} }
void Batch::render(const TargetRef& target, const Mat4x4f& matrix) 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 (!m_default_shader)
{ {
if (App::renderer() == Renderer::OpenGL) if (App::renderer().type == RendererType::OpenGL)
m_default_shader = Shader::create(opengl_shader_data); 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); m_default_shader = Shader::create(d3d11_shader_data);
} }

View File

@ -1,12 +1,16 @@
#include <blah/graphics/mesh.h> #include <blah/graphics/mesh.h>
#include "../internal/graphics.h" #include "../internal/renderer.h"
using namespace Blah; using namespace Blah;
MeshRef Mesh::create() 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<VertexAttribute> attributes, int stride) VertexFormat::VertexFormat(std::initializer_list<VertexAttribute> attributes, int stride)

View File

@ -1,13 +1,13 @@
#include <blah/graphics/renderpass.h> #include <blah/graphics/renderpass.h>
#include <blah/common.h> #include <blah/common.h>
#include "../internal/graphics.h" #include "../internal/renderer.h"
using namespace Blah; using namespace Blah;
RenderPass::RenderPass() RenderPass::RenderPass()
{ {
blend = BlendMode::Normal; blend = BlendMode::Normal;
target = App::backbuffer; target = App::backbuffer();
mesh = MeshRef(); mesh = MeshRef();
material = MaterialRef(); material = MaterialRef();
has_viewport = false; has_viewport = false;
@ -23,17 +23,21 @@ RenderPass::RenderPass()
void RenderPass::perform() void RenderPass::perform()
{ {
BLAH_ASSERT_RENDERER();
BLAH_ASSERT(material, "Trying to draw with an invalid Material"); BLAH_ASSERT(material, "Trying to draw with an invalid Material");
BLAH_ASSERT(material->shader(), "Trying to draw with an invalid Shader"); BLAH_ASSERT(material->shader(), "Trying to draw with an invalid Shader");
BLAH_ASSERT(mesh, "Trying to draw with an invalid Mesh"); BLAH_ASSERT(mesh, "Trying to draw with an invalid Mesh");
if (!Renderer::instance)
return;
// copy call // copy call
RenderPass pass = *this; RenderPass pass = *this;
// Validate Backbuffer // Validate Backbuffer
if (!pass.target) 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"); 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)); pass.scissor = pass.scissor.overlap_rect(Rectf(0, 0, draw_size.x, draw_size.y));
// perform render // perform render
Graphics::render(pass); Renderer::instance->render(pass);
} }

View File

@ -1,16 +1,20 @@
#include <blah/graphics/shader.h> #include <blah/graphics/shader.h>
#include <blah/app.h> #include <blah/app.h>
#include "../internal/graphics.h" #include "../internal/renderer.h"
using namespace Blah; using namespace Blah;
ShaderRef Shader::create(const ShaderData& data) ShaderRef Shader::create(const ShaderData& data)
{ {
BLAH_ASSERT_RENDERER();
BLAH_ASSERT(data.vertex.length() > 0, "Must provide a Vertex Shader"); 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.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 // validate the shader
if (shader) if (shader)

View File

@ -1,5 +1,5 @@
#include <blah/graphics/target.h> #include <blah/graphics/target.h>
#include "../internal/graphics.h" #include "../internal/renderer.h"
using namespace Blah; using namespace Blah;
@ -10,6 +10,7 @@ TargetRef Target::create(int width, int height)
TargetRef Target::create(int width, int height, const AttachmentFormats& textures) 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(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"); 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(depth_count <= 1, "Target can only have 1 Depth/Stencil Texture");
BLAH_ASSERT(color_count <= Attachments::capacity - 1, "Exceeded maximum Color texture count"); 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) TextureRef& Target::texture(int index)

View File

@ -2,7 +2,7 @@
#include <blah/images/image.h> #include <blah/images/image.h>
#include <blah/streams/stream.h> #include <blah/streams/stream.h>
#include <blah/common.h> #include <blah/common.h>
#include "../internal/graphics.h" #include "../internal/renderer.h"
using namespace Blah; 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) 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(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"); 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) if (tex && data != nullptr)
tex->set_data(data); tex->set_data(data);
return tex; return tex;
}
return TextureRef();
} }
TextureRef Texture::create(Stream& stream) TextureRef Texture::create(Stream& stream)

View File

@ -5,6 +5,7 @@
#include <blah/numerics/calc.h> #include <blah/numerics/calc.h>
#include "internal/input.h" #include "internal/input.h"
#include "internal/platform.h" #include "internal/platform.h"
#include <blah/graphics/target.h>
#include <cstring> #include <cstring>
using namespace Blah; using namespace Blah;
@ -124,8 +125,8 @@ void MouseState::on_move(const Vec2f& pos, const Vec2f& screen_pos)
position = pos; position = pos;
screen_position = screen_pos; screen_position = screen_pos;
Point size = Point(App::width(), App::height()); Point size = App::get_size();
Point draw = Point(App::draw_width(), App::draw_height()); Point draw = Point(App::backbuffer()->width(), App::backbuffer()->height());
draw_position.x = (position.x / (float)size.x) * draw.x; draw_position.x = (position.x / (float)size.x) * draw.x;
draw_position.y = (position.y / (float)size.y) * draw.y; draw_position.y = (position.y / (float)size.y) * draw.y;

View File

@ -1,60 +0,0 @@
#pragma once
#include <blah/app.h>
#include <blah/graphics/renderpass.h>
#include <blah/graphics/texture.h>
#include <blah/graphics/target.h>
#include <blah/graphics/shader.h>
#include <blah/graphics/mesh.h>
#include <blah/graphics/material.h>
#include <blah/numerics/color.h>
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();
}
}

View File

@ -1,215 +0,0 @@
#if !(defined(BLAH_GRAPHICS_OPENGL) || defined(BLAH_GRAPHICS_D3D11))
#include "graphics.h"
#include "platform.h"
#include <blah/common.h>
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<UniformInfo> m_uniforms;
public:
Dummy_Shader(const ShaderData* data)
{
}
virtual Vector<UniformInfo>& uniforms() override
{
return m_uniforms;
}
virtual const Vector<UniformInfo>& 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))

View File

@ -97,6 +97,9 @@ namespace Blah
// gets the contents of the clipboard into the given string // gets the contents of the clipboard into the given string
const char* get_clipboard(); const char* get_clipboard();
// Tries to open a URL in a web browser
void open_url(const char* url);
// OpenGL Methods // OpenGL Methods
void* gl_get_func(const char* name); void* gl_get_func(const char* name);
void* gl_context_create(); void* gl_context_create();
@ -105,8 +108,5 @@ namespace Blah
// D3D11 Methods // D3D11 Methods
void* d3d11_get_hwnd(); void* d3d11_get_hwnd();
// Tries to open a URL in a web browser
void open_url(const char* url);
} }
} }

View File

@ -2,7 +2,7 @@
#include "platform.h" #include "platform.h"
#include "input.h" #include "input.h"
#include "graphics.h" #include "renderer.h"
#include <blah/input.h> #include <blah/input.h>
#include <blah/app.h> #include <blah/app.h>
#include <blah/filesystem.h> #include <blah/filesystem.h>
@ -37,7 +37,7 @@ namespace Blah
char* base_path = nullptr; char* base_path = nullptr;
char* user_path = nullptr; char* user_path = nullptr;
bool displayed = false; bool displayed = false;
}; } g_platform;
// Blah SDL2 File // Blah SDL2 File
class SDL2File : public File class SDL2File : public File
@ -118,8 +118,6 @@ namespace Blah
using namespace Blah; using namespace Blah;
static Blah::SDL2Platform g_platform;
bool Platform::init(const Config& config) bool Platform::init(const Config& config)
{ {
g_platform = SDL2Platform(); 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; int flags = SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_RESIZABLE;
// enable OpenGL // enable OpenGL
if (App::renderer() == Renderer::OpenGL) if (config.renderer_type == RendererType::OpenGL)
{ {
flags |= SDL_WINDOW_OPENGL; flags |= SDL_WINDOW_OPENGL;
@ -217,7 +215,7 @@ void Platform::ready()
// enable V-Sync // enable V-Sync
// TODO: // TODO:
// This should be a toggle or controllable in some way // 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); SDL_GL_SetSwapInterval(1);
#endif #endif
} }
@ -454,7 +452,7 @@ void Platform::sleep(int milliseconds)
void Platform::present() void Platform::present()
{ {
if (App::renderer() == Renderer::OpenGL) if (App::renderer().type == RendererType::OpenGL)
{ {
SDL_GL_SwapWindow(g_platform.window); 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) 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); SDL_GL_GetDrawableSize(g_platform.window, width, height);
} }

View File

@ -1,14 +1,11 @@
#ifdef BLAH_PLATFORM_WIN32 #ifdef BLAH_PLATFORM_WIN32
// Note: // Note:
// This backend implementation is unfinished! // This is unfinished! It is missing Controller Support!
// It's missing a few things, namely:
// - Controller Support
// (And error testing)
#include "platform.h" #include "platform.h"
#include "input.h" #include "input.h"
#include "graphics.h" #include "renderer.h"
#include <blah/input.h> #include <blah/input.h>
#include <blah/app.h> #include <blah/app.h>
#include <blah/filesystem.h> #include <blah/filesystem.h>
@ -22,11 +19,20 @@
#include <shellapi.h> // for file explore #include <shellapi.h> // for file explore
#include <shlobj.h> // for known folder #include <shlobj.h> // for known folder
#include <chrono> // for ticks method #include <chrono> // for ticks method
#include <Xinput.h> // for XInput
namespace Blah namespace Blah
{ {
using Duration = std::chrono::system_clock::duration; 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 // Win32 Platform State
struct Win32Platform struct Win32Platform
{ {
@ -38,14 +44,34 @@ namespace Blah
RECT windowed_position; RECT windowed_position;
bool fullscreen = false; bool fullscreen = false;
InputState* input_state = nullptr; 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 // OpenGL Methods
// These are only loaded if built using the OpenGL Backend // These are only loaded if built using the OpenGL Backend
HMODULE opengl_dll; struct
void* (WINAPI* wglGetProcAddress) (const char* proc); {
HGLRC (WINAPI* wglCreateContext) (HDC hdc); HMODULE dll;
BOOL (WINAPI* wglDeleteContext) (HGLRC hglrc); wglGetProcAddress_fn get_proc_address;
BOOL (WINAPI* wglMakeCurrent) (HDC hdc, HGLRC hglrc); wglCreateContext_fn create_context;
wglDeleteContext_fn delete_context;
wglMakeCurrent_fn make_current;
} gl;
}; };
// Win32 File Class // Win32 File Class
@ -53,11 +79,16 @@ namespace Blah
{ {
private: private:
HANDLE m_handle; HANDLE m_handle;
LARGE_INTEGER m_size;
public: public:
Win32File(HANDLE handle) Win32File(HANDLE handle)
{ {
m_handle = handle; m_handle = handle;
LARGE_INTEGER file_size;
if (GetFileSizeEx(m_handle, &file_size))
m_size = file_size;
} }
~Win32File() ~Win32File()
@ -67,13 +98,7 @@ namespace Blah
size_t length() override size_t length() override
{ {
// Todo: cache this value? not sure how performant it is return m_size.QuadPart;
LARGE_INTEGER file_size;
if (GetFileSizeEx(m_handle, &file_size))
return file_size.QuadPart;
return 0;
} }
size_t position() override 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 // Converts Windows scancode to Blah key
Key win32_scancode_to_key(WPARAM wParam, LPARAM lParam); Key win32_scancode_to_key(WPARAM wParam, LPARAM lParam);
// Main Windows Procedure callback // Detects joysticks (connects & disconnects)
LRESULT CALLBACK win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); void win32_detect_joysticks();
} }
using namespace Blah; using namespace Blah;
@ -198,22 +226,30 @@ bool Platform::init(const Config& config)
return false; 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 // Create the OpenGL device info
if (App::renderer() == Renderer::OpenGL) if (config.renderer_type == RendererType::OpenGL)
{ {
// Load the DLL // Load the DLL
g_platform.opengl_dll = LoadLibraryA("opengl32.dll"); g_platform.gl.dll = LoadLibraryA("opengl32.dll");
if (g_platform.opengl_dll == NULL) if (g_platform.gl.dll == NULL)
{ {
Log::error("OpenGL Instantiation Failed - unable to fine opengl32.dll"); Log::error("OpenGL Instantiation Failed - unable to fine opengl32.dll");
return false; return false;
} }
// Get the Windows GL functions we need // Get the Windows GL functions we need
g_platform.wglGetProcAddress = (void* (WINAPI*)(const char*))GetProcAddress(g_platform.opengl_dll, "wglGetProcAddress"); g_platform.gl.get_proc_address = (wglGetProcAddress_fn)GetProcAddress(g_platform.gl.dll, "wglGetProcAddress");
g_platform.wglCreateContext = (HGLRC(WINAPI*) (HDC))GetProcAddress(g_platform.opengl_dll, "wglCreateContext"); g_platform.gl.create_context = (wglCreateContext_fn)GetProcAddress(g_platform.gl.dll, "wglCreateContext");
g_platform.wglDeleteContext = (BOOL(WINAPI*) (HGLRC))GetProcAddress(g_platform.opengl_dll, "wglDeleteContext"); g_platform.gl.delete_context = (wglDeleteContext_fn)GetProcAddress(g_platform.gl.dll, "wglDeleteContext");
g_platform.wglMakeCurrent = (BOOL(WINAPI*) (HDC, HGLRC))GetProcAddress(g_platform.opengl_dll, "wglMakeCurrent"); g_platform.gl.make_current = (wglMakeCurrent_fn)GetProcAddress(g_platform.gl.dll, "wglMakeCurrent");
// TODO: // TODO:
// Allow the user to apply (some of) these values before instantiation. // 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); SetPixelFormat(hdc, pixel_format, &pfd);
} }
// Reset our game timer // xinput api
g_platform.start_time = std::chrono::system_clock::now().time_since_epoch(); {
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 // Get Working Directory
{ {
@ -283,6 +336,9 @@ bool Platform::init(const Config& config)
CoTaskMemFree(path); CoTaskMemFree(path);
} }
// Reset our game timer
g_platform.start_time = std::chrono::system_clock::now().time_since_epoch();
// Not currently fullscreen // Not currently fullscreen
g_platform.fullscreen = false; g_platform.fullscreen = false;
@ -292,20 +348,18 @@ bool Platform::init(const Config& config)
void Platform::ready() 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 // Display the game window
ShowWindow(g_platform.hwnd, SW_SHOW); ShowWindow(g_platform.hwnd, SW_SHOW);
} }
void Platform::shutdown() 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); DestroyWindow(g_platform.hwnd);
} }
@ -334,6 +388,17 @@ LRESULT CALLBACK Blah::win32_window_procedure(HWND hwnd, UINT msg, WPARAM wParam
PostQuitMessage(0); PostQuitMessage(0);
return 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 // Mouse Input
case WM_LBUTTONDOWN: case WM_LBUTTONDOWN:
g_platform.input_state->mouse.on_press(MouseButton::Left); 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) void Platform::update(InputState& state)
{ {
// store reference to input state // store reference to input state
bool first_update = g_platform.input_state == nullptr;
g_platform.input_state = &state; 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 // Catch & Dispatch Window Messages
MSG msg; MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
@ -429,7 +499,7 @@ void Platform::sleep(int milliseconds)
void Platform::present() void Platform::present()
{ {
if (App::renderer() == Renderer::OpenGL) if (App::renderer().type == RendererType::OpenGL)
{ {
HDC hdc = GetDC(g_platform.hwnd); HDC hdc = GetDC(g_platform.hwnd);
SwapBuffers(hdc); SwapBuffers(hdc);
@ -463,6 +533,12 @@ void Platform::set_position(int x, int y)
SetWindowPos(g_platform.hwnd, NULL, x, y, w, h, 0); 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) void Platform::set_fullscreen(bool enabled)
{ {
if (g_platform.fullscreen == enabled) if (g_platform.fullscreen == enabled)
@ -628,30 +704,19 @@ FileRef Platform::file_open(const char* path, FileMode mode)
return FileRef(new Win32File(result)); 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) void* Platform::gl_get_func(const char* name)
{ {
// this check is taken from https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions // 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? // 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) || if ((p == 0) ||
(p == (void*)0x1) || (p == (void*)0x1) ||
(p == (void*)0x2) || (p == (void*)0x2) ||
(p == (void*)0x3) || (p == (void*)0x3) ||
(p == (void*)-1)) (p == (void*)-1))
{ {
p = (void*)GetProcAddress(g_platform.opengl_dll, name); p = (void*)GetProcAddress(g_platform.gl.dll, name);
} }
return p; return p;
@ -660,7 +725,7 @@ void* Platform::gl_get_func(const char* name)
void* Platform::gl_context_create() void* Platform::gl_context_create()
{ {
HDC hdc = GetDC(g_platform.hwnd); 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) void Platform::gl_context_make_current(void* context)
@ -668,15 +733,15 @@ void Platform::gl_context_make_current(void* context)
if (context != nullptr) if (context != nullptr)
{ {
HDC hdc = GetDC(g_platform.hwnd); HDC hdc = GetDC(g_platform.hwnd);
g_platform.wglMakeCurrent(hdc, (HGLRC)context); g_platform.gl.make_current(hdc, (HGLRC)context);
} }
else else
g_platform.wglMakeCurrent(NULL, NULL); g_platform.gl.make_current(NULL, NULL);
} }
void Platform::gl_context_destroy(void* context) void Platform::gl_context_destroy(void* context)
{ {
g_platform.wglDeleteContext((HGLRC)context); g_platform.gl.delete_context((HGLRC)context);
} }
void* Platform::d3d11_get_hwnd() void* Platform::d3d11_get_hwnd()
@ -864,4 +929,116 @@ Key Blah::win32_scancode_to_key(WPARAM wParam, LPARAM lParam)
return Key::Unknown; 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<const char*>(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 #endif // BLAH_PLATFORM_WIN32

90
src/internal/renderer.h Normal file
View File

@ -0,0 +1,90 @@
#pragma once
#include <blah/app.h>
#include <blah/graphics/renderpass.h>
#include <blah/graphics/texture.h>
#include <blah/graphics/target.h>
#include <blah/graphics/shader.h>
#include <blah/graphics/mesh.h>
#include <blah/graphics/material.h>
#include <blah/numerics/color.h>
#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
}
};
}

View File

@ -1,9 +1,9 @@
#ifdef BLAH_GRAPHICS_D3D11 #ifdef BLAH_RENDERER_D3D11
// TODO: // TODO:
// Note the D3D11 Implementation is still a work-in-progress // Note the D3D11 Implementation is still a work-in-progress
#include "graphics.h" #include "renderer.h"
#include "platform.h" #include "platform.h"
#include <blah/common.h> #include <blah/common.h>
#include <cstdio> #include <cstdio>
@ -15,12 +15,16 @@
#include <d3d11.h> #include <d3d11.h>
#include <d3dcompiler.h> #include <d3dcompiler.h>
// shorthand to our internal state
#define renderer ((Renderer_D3D11*)Renderer::instance)
namespace Blah namespace Blah
{ {
class D3D11_Shader; class D3D11_Shader;
struct D3D11 class Renderer_D3D11 : public Renderer
{ {
public:
// main resources // main resources
ID3D11Device* device = nullptr; ID3D11Device* device = nullptr;
ID3D11DeviceContext* context = nullptr; ID3D11DeviceContext* context = nullptr;
@ -28,9 +32,6 @@ namespace Blah
ID3D11RenderTargetView* backbuffer_view = nullptr; ID3D11RenderTargetView* backbuffer_view = nullptr;
ID3D11DepthStencilView* backbuffer_depth_view = nullptr; ID3D11DepthStencilView* backbuffer_depth_view = nullptr;
// supported renderer features
RendererFeatures features;
// last backbuffer size // last backbuffer size
Point last_size; Point last_size;
@ -72,6 +73,18 @@ namespace Blah
Vector<StoredSampler> sampler_cache; Vector<StoredSampler> sampler_cache;
Vector<StoredDepthStencil> depthstencil_cache; Vector<StoredDepthStencil> 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); ID3D11InputLayout* get_layout(D3D11_Shader* shader, const VertexFormat& format);
ID3D11BlendState* get_blend(const BlendMode& blend); ID3D11BlendState* get_blend(const BlendMode& blend);
ID3D11RasterizerState* get_rasterizer(const RenderPass& pass); ID3D11RasterizerState* get_rasterizer(const RenderPass& pass);
@ -79,17 +92,12 @@ namespace Blah
ID3D11DepthStencilState* get_depthstencil(const RenderPass& pass); ID3D11DepthStencilState* get_depthstencil(const RenderPass& pass);
}; };
// D3D11 State
D3D11 state;
// Utility Methods // Utility Methods
D3D11_BLEND_OP blend_op(BlendOp op); D3D11_BLEND_OP blend_op(BlendOp op);
D3D11_BLEND blend_factor(BlendFactor factor); D3D11_BLEND blend_factor(BlendFactor factor);
bool reflect_uniforms(Vector<UniformInfo>& append_uniforms_to, Vector<ID3D11Buffer*>& append_buffers_to, ID3DBlob* shader, ShaderType shader_type); bool reflect_uniforms(Vector<UniformInfo>& append_uniforms_to, Vector<ID3D11Buffer*>& append_buffers_to, ID3DBlob* shader, ShaderType shader_type);
void apply_uniforms(D3D11_Shader* shader, const MaterialRef& material, ShaderType type); void apply_uniforms(D3D11_Shader* shader, const MaterialRef& material, ShaderType type);
// ~ BEGIN IMPLEMENTATION ~
class D3D11_Texture : public Texture class D3D11_Texture : public Texture
{ {
private: private:
@ -160,7 +168,7 @@ namespace Blah
m_dxgi_format = desc.Format; 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 (!SUCCEEDED(hr))
{ {
if (texture) if (texture)
@ -171,7 +179,7 @@ namespace Blah
if (!is_depth_stencil) if (!is_depth_stencil)
{ {
hr = state.device->CreateShaderResourceView(texture, NULL, &view); hr = renderer->device->CreateShaderResourceView(texture, NULL, &view);
if (!SUCCEEDED(hr)) if (!SUCCEEDED(hr))
{ {
texture->Release(); texture->Release();
@ -220,7 +228,7 @@ namespace Blah
box.back = 1; box.back = 1;
// set data // set data
state.context->UpdateSubresource( renderer->context->UpdateSubresource(
texture, texture,
0, 0,
&box, &box,
@ -249,7 +257,7 @@ namespace Blah
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.MiscFlags = 0; desc.MiscFlags = 0;
hr = state.device->CreateTexture2D(&desc, NULL, &staging); hr = renderer->device->CreateTexture2D(&desc, NULL, &staging);
if (!SUCCEEDED(hr)) if (!SUCCEEDED(hr))
{ {
BLAH_ASSERT(false, "Failed to create staging texture to get data"); BLAH_ASSERT(false, "Failed to create staging texture to get data");
@ -266,7 +274,7 @@ namespace Blah
box.back = 1; box.back = 1;
// copy data to staging texture // copy data to staging texture
state.context->CopySubresourceRegion( renderer->context->CopySubresourceRegion(
staging, 0, staging, 0,
0, 0, 0, 0, 0, 0,
texture, 0, texture, 0,
@ -274,7 +282,7 @@ namespace Blah
// get data // get data
D3D11_MAPPED_SUBRESOURCE map; 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)) if (!SUCCEEDED(hr))
{ {
@ -287,7 +295,7 @@ namespace Blah
for (int y = 0; y < m_height; y++) for (int y = 0; y < m_height; y++)
memcpy(data + y * bytes_per_row, (unsigned char*)map.pData + map.RowPitch * y, bytes_per_row); 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 bool is_framebuffer() const override
@ -316,12 +324,12 @@ namespace Blah
if (attachments[i] == TextureFormat::DepthStencil) if (attachments[i] == TextureFormat::DepthStencil)
{ {
state.device->CreateDepthStencilView(tex->texture, nullptr, &depth_view); renderer->device->CreateDepthStencilView(tex->texture, nullptr, &depth_view);
} }
else else
{ {
ID3D11RenderTargetView* view = nullptr; ID3D11RenderTargetView* view = nullptr;
state.device->CreateRenderTargetView(tex->texture, nullptr, &view); renderer->device->CreateRenderTargetView(tex->texture, nullptr, &view);
color_views.push_back(view); color_views.push_back(view);
} }
} }
@ -355,7 +363,7 @@ namespace Blah
if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color) if (((int)mask & (int)ClearMask::Color) == (int)ClearMask::Color)
{ {
for (int i = 0; i < color_views.size(); i++) 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) if (depth_view)
@ -367,7 +375,7 @@ namespace Blah
flags |= D3D11_CLEAR_STENCIL; flags |= D3D11_CLEAR_STENCIL;
if (flags != 0) 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 // create vertex shader
{ {
hr = state.device->CreateVertexShader( hr = renderer->device->CreateVertexShader(
vertex_blob->GetBufferPointer(), vertex_blob->GetBufferPointer(),
vertex_blob->GetBufferSize(), vertex_blob->GetBufferSize(),
NULL, NULL,
@ -454,7 +462,7 @@ namespace Blah
// create fragment shader // create fragment shader
{ {
hr = state.device->CreatePixelShader( hr = renderer->device->CreatePixelShader(
fragment_blob->GetBufferPointer(), fragment_blob->GetBufferPointer(),
fragment_blob->GetBufferSize(), fragment_blob->GetBufferSize(),
NULL, NULL,
@ -607,7 +615,7 @@ namespace Blah
data.pSysMem = indices; data.pSysMem = indices;
// create // 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"); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Index Data");
} }
} }
@ -615,13 +623,13 @@ namespace Blah
{ {
D3D11_MAPPED_SUBRESOURCE map; 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"); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Index Data");
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
memcpy(map.pData, indices, index_stride * count); 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; data.pSysMem = vertices;
// create // 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"); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Vertex Data");
} }
} }
@ -663,13 +671,13 @@ namespace Blah
else if (vertices) else if (vertices)
{ {
D3D11_MAPPED_SUBRESOURCE map; 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"); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Vertex Data");
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
memcpy(map.pData, vertices, vertex_format.stride * count); 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(); last_size = Point(App::backbuffer()->width(), App::backbuffer()->height());
state.last_size = Point(App::draw_width(), App::draw_height());
// Define Swap Chain // Define Swap Chain
DXGI_SWAP_CHAIN_DESC desc = {}; DXGI_SWAP_CHAIN_DESC desc = {};
@ -729,21 +736,21 @@ namespace Blah
0, 0,
D3D11_SDK_VERSION, D3D11_SDK_VERSION,
&desc, &desc,
&state.swap_chain, &swap_chain,
&state.device, &device,
&feature_level, &feature_level,
&state.context); &context);
// Exit out if it's not OK // 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; return false;
// Get the backbuffer // Get the backbuffer
ID3D11Texture2D* frame_buffer = nullptr; 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) if (frame_buffer)
{ {
state.device->CreateRenderTargetView(frame_buffer, nullptr, &state.backbuffer_view); device->CreateRenderTargetView(frame_buffer, nullptr, &backbuffer_view);
frame_buffer->Release(); frame_buffer->Release();
} }
@ -751,9 +758,10 @@ namespace Blah
// create a depth backbuffer // create a depth backbuffer
// Store Features // Store Features
state.features.instancing = true; features.type = RendererType::D3D11;
state.features.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION; features.instancing = true;
state.features.origin_bottom_left = false; features.max_texture_size = D3D11_REQ_TEXTURE2D_U_OR_V_DIMENSION;
features.origin_bottom_left = false;
// Print Driver Info // Print Driver Info
{ {
@ -761,7 +769,7 @@ namespace Blah
IDXGIAdapter* dxgi_adapter; IDXGIAdapter* dxgi_adapter;
DXGI_ADAPTER_DESC adapter_desc; 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)) if (SUCCEEDED(hr))
{ {
@ -782,85 +790,72 @@ namespace Blah
return true; return true;
} }
Renderer Graphics::renderer() void Renderer_D3D11::shutdown()
{
return Renderer::D3D11;
}
void Graphics::shutdown()
{ {
// release cached objects // release cached objects
for (auto& it : state.blend_cache) for (auto& it : blend_cache)
it.state->Release(); it.state->Release();
for (auto& it : state.depthstencil_cache) for (auto& it : depthstencil_cache)
it.state->Release(); it.state->Release();
for (auto& it : state.layout_cache) for (auto& it : layout_cache)
it.layout->Release(); it.layout->Release();
for (auto& it : state.rasterizer_cache) for (auto& it : rasterizer_cache)
it.state->Release(); it.state->Release();
for (auto& it : state.sampler_cache) for (auto& it : sampler_cache)
it.state->Release(); it.state->Release();
// release main devices // release main devices
if (state.backbuffer_view) if (backbuffer_view)
state.backbuffer_view->Release(); backbuffer_view->Release();
if (state.backbuffer_depth_view) if (backbuffer_depth_view)
state.backbuffer_depth_view->Release(); backbuffer_depth_view->Release();
state.swap_chain->Release(); swap_chain->Release();
state.context->ClearState(); context->ClearState();
state.context->Flush(); context->Flush();
state.context->Release(); context->Release();
state.device->Release(); device->Release();
// reset state
state = D3D11();
} }
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; HRESULT hr;
auto next_size = Point(App::draw_width(), App::draw_height()); auto next_size = Point(App::backbuffer()->width(), App::backbuffer()->height());
if (state.last_size != next_size) if (last_size != next_size)
{ {
// release old buffer // release old buffer
if (state.backbuffer_view) if (backbuffer_view)
state.backbuffer_view->Release(); backbuffer_view->Release();
// perform resize // 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"); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Backbuffer on Resize");
state.last_size = next_size; last_size = next_size;
// get the new buffer // get the new buffer
ID3D11Texture2D* frame_buffer = nullptr; 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) 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"); BLAH_ASSERT(SUCCEEDED(hr), "Failed to update Backbuffer on Resize");
frame_buffer->Release(); 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"); 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); auto result = new D3D11_Texture(width, height, format, false);
@ -871,12 +866,12 @@ namespace Blah
return TextureRef(); 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)); 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); auto result = new D3D11_Shader(data);
if (result->valid) if (result->valid)
@ -886,23 +881,23 @@ namespace Blah
return ShaderRef(); return ShaderRef();
} }
MeshRef Graphics::create_mesh() MeshRef Renderer_D3D11::create_mesh()
{ {
return MeshRef(new D3D11_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 mesh = (D3D11_Mesh*)pass.mesh.get();
auto shader = (D3D11_Shader*)(pass.material->shader().get()); auto shader = (D3D11_Shader*)(pass.material->shader().get());
// OM // OM
{ {
// Set the Target // 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 else
{ {
@ -912,14 +907,14 @@ namespace Blah
// Depth // Depth
{ {
auto depthstencil = state.get_depthstencil(pass); auto depthstencil = get_depthstencil(pass);
if (depthstencil) if (depthstencil)
ctx->OMSetDepthStencilState(depthstencil, 0); ctx->OMSetDepthStencilState(depthstencil, 0);
} }
// Blend Mode // Blend Mode
{ {
auto blend = state.get_blend(pass.blend); auto blend = get_blend(pass.blend);
if (blend) if (blend)
{ {
auto color = Color::from_rgba(pass.blend.rgba); auto color = Color::from_rgba(pass.blend.rgba);
@ -941,7 +936,7 @@ namespace Blah
ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Assign Layout // Assign Layout
auto layout = state.get_layout(shader, mesh->vertex_format); auto layout = get_layout(shader, mesh->vertex_format);
ctx->IASetInputLayout(layout); ctx->IASetInputLayout(layout);
// Assign Vertex Buffer // Assign Vertex Buffer
@ -1000,7 +995,7 @@ namespace Blah
auto& samplers = pass.material->samplers(); auto& samplers = pass.material->samplers();
for (int i = 0; i < samplers.size(); i++) for (int i = 0; i < samplers.size(); i++)
{ {
auto sampler = state.get_sampler(samplers[i]); auto sampler = get_sampler(samplers[i]);
if (sampler) if (sampler)
ctx->PSSetSamplers(i, 1, &sampler); ctx->PSSetSamplers(i, 1, &sampler);
} }
@ -1034,7 +1029,7 @@ namespace Blah
// Rasterizer // Rasterizer
{ {
auto rasterizer = state.get_rasterizer(pass); auto rasterizer = get_rasterizer(pass);
if (rasterizer) if (rasterizer)
ctx->RSSetState(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) 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 }; 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; UINT flags = 0;
if (((int)mask & (int)ClearMask::Depth) == (int)ClearMask::Depth) if (((int)mask & (int)ClearMask::Depth) == (int)ClearMask::Depth)
@ -1082,7 +1077,7 @@ namespace Blah
flags |= D3D11_CLEAR_STENCIL; flags |= D3D11_CLEAR_STENCIL;
if (flags != 0) 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; buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
ID3D11Buffer* buffer; ID3D11Buffer* buffer;
state.device->CreateBuffer(&buffer_desc, nullptr, &buffer); renderer->device->CreateBuffer(&buffer_desc, nullptr, &buffer);
append_buffers_to.push_back(buffer); append_buffers_to.push_back(buffer);
} }
@ -1278,14 +1273,14 @@ namespace Blah
if (buffers[i]) if (buffers[i])
{ {
D3D11_MAPPED_SUBRESOURCE map; 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)); 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 // find existing
for (auto& it : layout_cache) for (auto& it : layout_cache)
@ -1374,7 +1369,7 @@ namespace Blah
return nullptr; return nullptr;
} }
ID3D11BlendState* D3D11::get_blend(const BlendMode& blend) ID3D11BlendState* Renderer_D3D11::get_blend(const BlendMode& blend)
{ {
for (auto& it : blend_cache) for (auto& it : blend_cache)
if (it.blend == blend) if (it.blend == blend)
@ -1414,7 +1409,7 @@ namespace Blah
desc.RenderTarget[i] = desc.RenderTarget[0]; desc.RenderTarget[i] = desc.RenderTarget[0];
ID3D11BlendState* blend_state = nullptr; ID3D11BlendState* blend_state = nullptr;
auto hr = state.device->CreateBlendState(&desc, &blend_state); auto hr = renderer->device->CreateBlendState(&desc, &blend_state);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
@ -1427,7 +1422,7 @@ namespace Blah
return nullptr; return nullptr;
} }
ID3D11SamplerState* D3D11::get_sampler(const TextureSampler& sampler) ID3D11SamplerState* Renderer_D3D11::get_sampler(const TextureSampler& sampler)
{ {
for (auto& it : sampler_cache) for (auto& it : sampler_cache)
if (it.sampler == sampler) if (it.sampler == sampler)
@ -1462,7 +1457,7 @@ namespace Blah
} }
ID3D11SamplerState* result; ID3D11SamplerState* result;
auto hr = state.device->CreateSamplerState(&desc, &result); auto hr = renderer->device->CreateSamplerState(&desc, &result);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
@ -1475,7 +1470,7 @@ namespace Blah
return nullptr; return nullptr;
} }
ID3D11RasterizerState* D3D11::get_rasterizer(const RenderPass& pass) ID3D11RasterizerState* Renderer_D3D11::get_rasterizer(const RenderPass& pass)
{ {
for (auto& it : rasterizer_cache) for (auto& it : rasterizer_cache)
if (it.cull == pass.cull && it.has_scissor == pass.has_scissor) if (it.cull == pass.cull && it.has_scissor == pass.has_scissor)
@ -1502,7 +1497,7 @@ namespace Blah
desc.AntialiasedLineEnable = false; desc.AntialiasedLineEnable = false;
ID3D11RasterizerState* result; ID3D11RasterizerState* result;
auto hr = state.device->CreateRasterizerState(&desc, &result); auto hr = renderer->device->CreateRasterizerState(&desc, &result);
if (SUCCEEDED(hr)) if (SUCCEEDED(hr))
{ {
@ -1516,7 +1511,7 @@ namespace Blah
return nullptr; return nullptr;
} }
ID3D11DepthStencilState* D3D11::get_depthstencil(const RenderPass& pass) ID3D11DepthStencilState* Renderer_D3D11::get_depthstencil(const RenderPass& pass)
{ {
for (auto& it : depthstencil_cache) for (auto& it : depthstencil_cache)
if (it.depth == pass.depth) if (it.depth == pass.depth)
@ -1541,7 +1536,7 @@ namespace Blah
} }
ID3D11DepthStencilState* result; ID3D11DepthStencilState* result;
auto hr = state.device->CreateDepthStencilState(&desc, &result); auto hr = renderer->device->CreateDepthStencilState(&desc, &result);
if (SUCCEEDED(hr)) 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

View File

@ -1,6 +1,6 @@
#ifdef BLAH_GRAPHICS_OPENGL #ifdef BLAH_RENDERER_OPENGL
#include "graphics.h" #include "renderer.h"
#include "platform.h" #include "platform.h"
#include <blah/common.h> #include <blah/common.h>
#include <stdio.h> #include <stdio.h>
@ -338,14 +338,22 @@ typedef void (APIENTRY* DEBUGPROC)(GLenum source,
const GLchar* message, const GLchar* message,
const void* userParam); const void* userParam);
// shorthand to our internal state
#define renderer ((Renderer_OpenGL*)Renderer::instance)
namespace Blah namespace Blah
{ {
struct State class Renderer_OpenGL : public Renderer
{
public:
struct Bindings
{ {
// GL function pointers // GL function pointers
#define GL_FUNC(name, ret, ...) typedef ret (*name ## Func) (__VA_ARGS__); name ## Func name; #define GL_FUNC(name, ret, ...) typedef ret (*name ## Func) (__VA_ARGS__); name ## Func name;
GL_FUNCTIONS GL_FUNCTIONS
#undef GL_FUNC #undef GL_FUNC
} gl;
// state // state
void* context; void* context;
@ -358,11 +366,19 @@ namespace Blah
int max_samples; int max_samples;
int max_texture_image_units; int max_texture_image_units;
int max_texture_size; int max_texture_size;
RendererFeatures features;
};
// static state bool init() override;
State gl; 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 // debug callback
void APIENTRY gl_message_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) 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) GLuint gl_mesh_assign_attributes(GLuint buffer, GLenum buffer_type, const VertexFormat& format, GLint divisor)
{ {
// bind // bind
gl.BindBuffer(buffer_type, buffer); renderer->gl.BindBuffer(buffer_type, buffer);
// TODO: disable existing enabled attributes .. // TODO: disable existing enabled attributes ..
// ... // ...
@ -485,9 +501,9 @@ namespace Blah
} }
u32 location = (u32)(attribute.index); u32 location = (u32)(attribute.index);
gl.EnableVertexAttribArray(location); renderer->gl.EnableVertexAttribArray(location);
gl.VertexAttribPointer(location, components, type, attribute.normalized, format.stride, (void*)ptr); renderer->gl.VertexAttribPointer(location, components, type, attribute.normalized, format.stride, (void*)ptr);
gl.VertexAttribDivisor(location, divisor); renderer->gl.VertexAttribDivisor(location, divisor);
ptr += components * component_size; ptr += components * component_size;
} }
@ -565,9 +581,9 @@ namespace Blah
m_gl_format = GL_RED; m_gl_format = GL_RED;
m_gl_type = GL_UNSIGNED_BYTE; 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; return;
} }
@ -601,16 +617,16 @@ namespace Blah
return; return;
} }
gl.GenTextures(1, &m_id); renderer->gl.GenTextures(1, &m_id);
gl.ActiveTexture(GL_TEXTURE0); renderer->gl.ActiveTexture(GL_TEXTURE0);
gl.BindTexture(GL_TEXTURE_2D, m_id); renderer->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.TexImage2D(GL_TEXTURE_2D, 0, m_gl_internal_format, width, height, 0, m_gl_format, m_gl_type, nullptr);
} }
~OpenGL_Texture() ~OpenGL_Texture()
{ {
if (m_id > 0) if (m_id > 0 && renderer)
gl.DeleteTextures(1, &m_id); renderer->gl.DeleteTextures(1, &m_id);
} }
GLuint gl_id() const GLuint gl_id() const
@ -639,26 +655,26 @@ namespace Blah
{ {
m_sampler = sampler; m_sampler = sampler;
gl.BindTexture(GL_TEXTURE_2D, m_id); renderer->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)); renderer->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)); renderer->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)); renderer->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.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 virtual void set_data(unsigned char* data) override
{ {
gl.ActiveTexture(GL_TEXTURE0); renderer->gl.ActiveTexture(GL_TEXTURE0);
gl.BindTexture(GL_TEXTURE_2D, m_id); renderer->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.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 virtual void get_data(unsigned char* data) override
{ {
gl.ActiveTexture(GL_TEXTURE0); renderer->gl.ActiveTexture(GL_TEXTURE0);
gl.BindTexture(GL_TEXTURE_2D, m_id); renderer->gl.BindTexture(GL_TEXTURE_2D, m_id);
gl.GetTexImage(GL_TEXTURE_2D, 0, m_gl_internal_format, m_gl_type, data); renderer->gl.GetTexImage(GL_TEXTURE_2D, 0, m_gl_internal_format, m_gl_type, data);
} }
virtual bool is_framebuffer() const override virtual bool is_framebuffer() const override
@ -680,11 +696,11 @@ namespace Blah
OpenGL_Target(int width, int height, const TextureFormat* attachments, int attachmentCount) 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_width = width;
m_height = height; m_height = height;
gl.BindFramebuffer(GL_FRAMEBUFFER, m_id); renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, m_id);
for (int i = 0; i < attachmentCount; i++) for (int i = 0; i < attachmentCount; i++)
{ {
@ -696,20 +712,20 @@ namespace Blah
if (attachments[i] != TextureFormat::DepthStencil) 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 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() ~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; m_id = 0;
} }
} }
@ -740,13 +756,13 @@ namespace Blah
if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil) if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil)
clear |= GL_STENCIL_BUFFER_BIT; clear |= GL_STENCIL_BUFFER_BIT;
gl.BindFramebuffer(GL_FRAMEBUFFER, m_id); renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, m_id);
gl.Disable(GL_SCISSOR_TEST); renderer->gl.Disable(GL_SCISSOR_TEST);
gl.ColorMask(true, true, true, true); renderer->gl.ColorMask(true, true, true, true);
gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f); renderer->gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f);
gl.ClearDepth(depth); renderer->gl.ClearDepth(depth);
gl.ClearStencil(stencil); renderer->gl.ClearStencil(stencil);
gl.Clear(clear); renderer->gl.Clear(clear);
} }
}; };
@ -778,47 +794,47 @@ namespace Blah
GLchar log[1024]; GLchar log[1024];
GLsizei log_length = 0; 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(); const GLchar* source = (const GLchar*)data->vertex.cstr();
gl.ShaderSource(vertex_shader, 1, &source, nullptr); renderer->gl.ShaderSource(vertex_shader, 1, &source, nullptr);
gl.CompileShader(vertex_shader); renderer->gl.CompileShader(vertex_shader);
gl.GetShaderInfoLog(vertex_shader, 1024, &log_length, log); renderer->gl.GetShaderInfoLog(vertex_shader, 1024, &log_length, log);
if (log_length > 0) if (log_length > 0)
{ {
gl.DeleteShader(vertex_shader); renderer->gl.DeleteShader(vertex_shader);
Log::error(log); Log::error(log);
return; 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(); const GLchar* source = (const GLchar*)data->fragment.cstr();
gl.ShaderSource(fragment_shader, 1, &source, nullptr); renderer->gl.ShaderSource(fragment_shader, 1, &source, nullptr);
gl.CompileShader(fragment_shader); renderer->gl.CompileShader(fragment_shader);
gl.GetShaderInfoLog(fragment_shader, 1024, &log_length, log); renderer->gl.GetShaderInfoLog(fragment_shader, 1024, &log_length, log);
if (log_length > 0) if (log_length > 0)
{ {
gl.DeleteShader(vertex_shader); renderer->gl.DeleteShader(vertex_shader);
gl.DeleteShader(fragment_shader); renderer->gl.DeleteShader(fragment_shader);
Log::error(log); Log::error(log);
return; return;
} }
} }
// create actual shader program // create actual shader program
GLuint id = gl.CreateProgram(); GLuint id = renderer->gl.CreateProgram();
gl.AttachShader(id, vertex_shader); renderer->gl.AttachShader(id, vertex_shader);
gl.AttachShader(id, fragment_shader); renderer->gl.AttachShader(id, fragment_shader);
gl.LinkProgram(id); renderer->gl.LinkProgram(id);
gl.GetProgramInfoLog(id, 1024, &log_length, log); renderer->gl.GetProgramInfoLog(id, 1024, &log_length, log);
gl.DetachShader(id, vertex_shader); renderer->gl.DetachShader(id, vertex_shader);
gl.DetachShader(id, fragment_shader); renderer->gl.DetachShader(id, fragment_shader);
gl.DeleteShader(vertex_shader); renderer->gl.DeleteShader(vertex_shader);
gl.DeleteShader(fragment_shader); renderer->gl.DeleteShader(fragment_shader);
if (log_length > 0) if (log_length > 0)
{ {
@ -832,7 +848,7 @@ namespace Blah
const int max_name_length = 256; const int max_name_length = 256;
GLint active_uniforms = 0; 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++) for (int i = 0; i < active_uniforms; i++)
{ {
@ -841,7 +857,7 @@ namespace Blah
GLenum type; GLenum type;
GLchar name[max_name_length + 1]; 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'; name[length] = '\0';
// array names end with "[0]", and we don't want that // array names end with "[0]", and we don't want that
@ -863,7 +879,7 @@ namespace Blah
tex_uniform.array_length = size; tex_uniform.array_length = size;
tex_uniform.type = UniformType::Texture2D; tex_uniform.type = UniformType::Texture2D;
tex_uniform.shader = ShaderType::Fragment; 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); m_uniforms.push_back(tex_uniform);
UniformInfo sampler_uniform; UniformInfo sampler_uniform;
@ -872,7 +888,7 @@ namespace Blah
sampler_uniform.array_length = size; sampler_uniform.array_length = size;
sampler_uniform.type = UniformType::Sampler2D; sampler_uniform.type = UniformType::Sampler2D;
sampler_uniform.shader = ShaderType::Fragment; 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); m_uniforms.push_back(sampler_uniform);
} }
else else
@ -882,7 +898,7 @@ namespace Blah
uniform.type = UniformType::None; uniform.type = UniformType::None;
uniform.buffer_index = 0; uniform.buffer_index = 0;
uniform.array_length = size; 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); uniform.shader = (ShaderType)((int)ShaderType::Vertex | (int)ShaderType::Fragment);
if (type == GL_FLOAT) if (type == GL_FLOAT)
@ -912,15 +928,15 @@ namespace Blah
// assign ID if the uniforms were valid // assign ID if the uniforms were valid
if (!valid_uniforms) if (!valid_uniforms)
gl.DeleteProgram(id); renderer->gl.DeleteProgram(id);
else else
m_id = id; m_id = id;
} }
~OpenGL_Shader() ~OpenGL_Shader()
{ {
if (m_id > 0) if (m_id > 0 && renderer)
gl.DeleteProgram(m_id); renderer->gl.DeleteProgram(m_id);
m_id = 0; m_id = 0;
} }
@ -975,19 +991,22 @@ namespace Blah
m_vertex_attribs_enabled = 0; m_vertex_attribs_enabled = 0;
m_instance_attribs_enabled = 0; m_instance_attribs_enabled = 0;
gl.GenVertexArrays(1, &m_id); renderer->gl.GenVertexArrays(1, &m_id);
} }
~OpenGL_Mesh() ~OpenGL_Mesh()
{
if (renderer)
{ {
if (m_vertex_buffer != 0) if (m_vertex_buffer != 0)
gl.DeleteBuffers(1, &m_vertex_buffer); renderer->gl.DeleteBuffers(1, &m_vertex_buffer);
if (m_index_buffer != 0) if (m_index_buffer != 0)
gl.DeleteBuffers(1, &m_index_buffer); renderer->gl.DeleteBuffers(1, &m_index_buffer);
if (m_instance_buffer != 0) if (m_instance_buffer != 0)
gl.DeleteBuffers(1, &m_instance_buffer); renderer->gl.DeleteBuffers(1, &m_instance_buffer);
if (m_id != 0) if (m_id != 0)
gl.DeleteVertexArrays(1, &m_id); renderer->gl.DeleteVertexArrays(1, &m_id);
}
m_id = 0; m_id = 0;
} }
@ -1010,10 +1029,10 @@ namespace Blah
{ {
m_index_count = count; m_index_count = count;
gl.BindVertexArray(m_id); renderer->gl.BindVertexArray(m_id);
{ {
if (m_index_buffer == 0) if (m_index_buffer == 0)
gl.GenBuffers(1, &(m_index_buffer)); renderer->gl.GenBuffers(1, &(m_index_buffer));
switch (format) switch (format)
{ {
@ -1027,52 +1046,52 @@ namespace Blah
break; break;
} }
gl.BindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer); renderer->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.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 virtual void vertex_data(const VertexFormat& format, const void* vertices, i64 count) override
{ {
m_vertex_count = count; m_vertex_count = count;
gl.BindVertexArray(m_id); renderer->gl.BindVertexArray(m_id);
{ {
// Create Buffer if it doesn't exist yet // Create Buffer if it doesn't exist yet
if (m_vertex_buffer == 0) if (m_vertex_buffer == 0)
gl.GenBuffers(1, &(m_vertex_buffer)); renderer->gl.GenBuffers(1, &(m_vertex_buffer));
// TODO: // TODO:
// Cache this // Cache this
m_vertex_size = gl_mesh_assign_attributes(m_vertex_buffer, GL_ARRAY_BUFFER, format, 0); m_vertex_size = gl_mesh_assign_attributes(m_vertex_buffer, GL_ARRAY_BUFFER, format, 0);
// Upload Buffer // Upload Buffer
gl.BindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer); renderer->gl.BindBuffer(GL_ARRAY_BUFFER, m_vertex_buffer);
gl.BufferData(GL_ARRAY_BUFFER, m_vertex_size * count, vertices, GL_DYNAMIC_DRAW); 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 virtual void instance_data(const VertexFormat& format, const void* instances, i64 count) override
{ {
m_instance_count = count; m_instance_count = count;
gl.BindVertexArray(m_id); renderer->gl.BindVertexArray(m_id);
{ {
// Create Buffer if it doesn't exist yet // Create Buffer if it doesn't exist yet
if (m_instance_buffer == 0) if (m_instance_buffer == 0)
gl.GenBuffers(1, &(m_instance_buffer)); renderer->gl.GenBuffers(1, &(m_instance_buffer));
// TODO: // TODO:
// Cache this // Cache this
m_instance_size = gl_mesh_assign_attributes(m_instance_buffer, GL_ARRAY_BUFFER, format, 1); m_instance_size = gl_mesh_assign_attributes(m_instance_buffer, GL_ARRAY_BUFFER, format, 1);
// Upload Buffer // Upload Buffer
gl.BindBuffer(GL_ARRAY_BUFFER, m_instance_buffer); renderer->gl.BindBuffer(GL_ARRAY_BUFFER, m_instance_buffer);
gl.BufferData(GL_ARRAY_BUFFER, m_instance_size * count, instances, GL_DYNAMIC_DRAW); 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 virtual i64 index_count() const override
@ -1091,21 +1110,19 @@ namespace Blah
} }
}; };
bool Graphics::init() bool Renderer_OpenGL::init()
{ {
gl = State();
// create gl context // create gl context
gl.context = Platform::gl_context_create(); context = Platform::gl_context_create();
if (gl.context == nullptr) if (context == nullptr)
{ {
Log::error("Failed to create OpenGL Context"); Log::error("Failed to create OpenGL Context");
return false; return false;
} }
Platform::gl_context_make_current(gl.context); Platform::gl_context_make_current(context);
// bind opengl functions // 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 GL_FUNCTIONS
#undef GL_FUNC #undef GL_FUNC
@ -1118,13 +1135,13 @@ namespace Blah
} }
// get opengl info // get opengl info
gl.GetIntegerv(0x8CDF, &gl.max_color_attachments); gl.GetIntegerv(0x8CDF, &max_color_attachments);
gl.GetIntegerv(0x80E9, &gl.max_element_indices); gl.GetIntegerv(0x80E9, &max_element_indices);
gl.GetIntegerv(0x80E8, &gl.max_element_vertices); gl.GetIntegerv(0x80E8, &max_element_vertices);
gl.GetIntegerv(0x84E8, &gl.max_renderbuffer_size); gl.GetIntegerv(0x84E8, &max_renderbuffer_size);
gl.GetIntegerv(0x8D57, &gl.max_samples); gl.GetIntegerv(0x8D57, &max_samples);
gl.GetIntegerv(0x8872, &gl.max_texture_image_units); gl.GetIntegerv(0x8872, &max_texture_image_units);
gl.GetIntegerv(0x0D33, &gl.max_texture_size); gl.GetIntegerv(0x0D33, &max_texture_size);
// log // log
Log::info("OpenGL %s, %s", Log::info("OpenGL %s, %s",
@ -1136,34 +1153,25 @@ namespace Blah
gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1); gl.PixelStorei(GL_UNPACK_ALIGNMENT, 1);
// assign info // assign info
gl.features.instancing = true; features.type = RendererType::OpenGL;
gl.features.origin_bottom_left = true; features.instancing = true;
gl.features.max_texture_size = gl.max_texture_size; features.origin_bottom_left = true;
features.max_texture_size = max_texture_size;
return true; return true;
} }
Renderer Graphics::renderer() void Renderer_OpenGL::shutdown()
{ {
return Renderer::OpenGL; Platform::gl_context_destroy(context);
context = nullptr;
} }
void Graphics::shutdown() void Renderer_OpenGL::update() {}
{ void Renderer_OpenGL::before_render() {}
Platform::gl_context_destroy(gl.context); void Renderer_OpenGL::after_render() {}
gl.context = nullptr;
}
const RendererFeatures& Graphics::features() TextureRef Renderer_OpenGL::create_texture(int width, int height, TextureFormat format)
{
return gl.features;
}
void Graphics::update() {}
void Graphics::before_render() {}
void Graphics::after_render() {}
TextureRef Graphics::create_texture(int width, int height, TextureFormat format)
{ {
auto resource = new OpenGL_Texture(width, height, format); auto resource = new OpenGL_Texture(width, height, format);
@ -1176,7 +1184,7 @@ namespace Blah
return TextureRef(resource); 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); auto resource = new OpenGL_Target(width, height, attachments, attachmentCount);
@ -1189,7 +1197,7 @@ namespace Blah
return TargetRef(resource); return TargetRef(resource);
} }
ShaderRef Graphics::create_shader(const ShaderData* data) ShaderRef Renderer_OpenGL::create_shader(const ShaderData* data)
{ {
auto resource = new OpenGL_Shader(data); auto resource = new OpenGL_Shader(data);
@ -1202,7 +1210,7 @@ namespace Blah
return ShaderRef(resource); return ShaderRef(resource);
} }
MeshRef Graphics::create_mesh() MeshRef Renderer_OpenGL::create_mesh()
{ {
auto resource = new OpenGL_Mesh(); auto resource = new OpenGL_Mesh();
@ -1215,24 +1223,20 @@ namespace Blah
return MeshRef(resource); return MeshRef(resource);
} }
void Graphics::render(const RenderPass& pass) void Renderer_OpenGL::render(const RenderPass& pass)
{ {
// Bind the Target // Bind the Target
Point size; if (pass.target == App::backbuffer())
if (pass.target == App::backbuffer)
{ {
gl.BindFramebuffer(GL_FRAMEBUFFER, 0); renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
size.x = App::draw_width();
size.y = App::draw_height();
} }
else if (pass.target) else if (pass.target)
{ {
auto framebuffer = (OpenGL_Target*)pass.target.get(); auto framebuffer = (OpenGL_Target*)pass.target.get();
gl.BindFramebuffer(GL_FRAMEBUFFER, framebuffer->gl_id()); renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, framebuffer->gl_id());
size.x = pass.target->width();
size.y = pass.target->height();
} }
auto size = Point(pass.target->width(), pass.target->height());
auto shader_ref = pass.material->shader(); auto shader_ref = pass.material->shader();
auto shader = (OpenGL_Shader*)shader_ref.get(); auto shader = (OpenGL_Shader*)shader_ref.get();
auto mesh = (OpenGL_Mesh*)pass.mesh.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: I don't love how material values are assigned or set here
// TODO: this should be cached? // TODO: this should be cached?
{ {
gl.UseProgram(shader->gl_id()); renderer->gl.UseProgram(shader->gl_id());
int texture_slot = 0; int texture_slot = 0;
int gl_texture_slot = 0; int gl_texture_slot = 0;
@ -1266,24 +1270,24 @@ namespace Blah
auto tex = pass.material->get_texture(texture_slot, n); auto tex = pass.material->get_texture(texture_slot, n);
auto sampler = pass.material->get_sampler(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) if (!tex)
{ {
gl.BindTexture(GL_TEXTURE_2D, 0); renderer->gl.BindTexture(GL_TEXTURE_2D, 0);
} }
else else
{ {
auto gl_tex = ((OpenGL_Texture*)tex.get()); auto gl_tex = ((OpenGL_Texture*)tex.get());
gl_tex->update_sampler(sampler); 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; texture_ids[n] = gl_texture_slot;
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++; texture_slot++;
continue; continue;
} }
@ -1291,37 +1295,37 @@ namespace Blah
// Float // Float
if (uniform.type == UniformType::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; data += uniform.array_length;
} }
// Float2 // Float2
else if (uniform.type == UniformType::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; data += 2 * uniform.array_length;
} }
// Float3 // Float3
else if (uniform.type == UniformType::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; data += 3 * uniform.array_length;
} }
// Float4 // Float4
else if (uniform.type == UniformType::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; data += 4 * uniform.array_length;
} }
// Matrix3x2 // Matrix3x2
else if (uniform.type == UniformType::Mat3x2) 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; data += 6 * uniform.array_length;
} }
// Matrix4x4 // Matrix4x4
else if (uniform.type == UniformType::Mat4x4) 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; data += 16 * uniform.array_length;
} }
} }
@ -1336,11 +1340,11 @@ namespace Blah
GLenum alphaSrc = gl_get_blend_factor(pass.blend.alpha_src); GLenum alphaSrc = gl_get_blend_factor(pass.blend.alpha_src);
GLenum alphaDst = gl_get_blend_factor(pass.blend.alpha_dst); GLenum alphaDst = gl_get_blend_factor(pass.blend.alpha_dst);
gl.Enable(GL_BLEND); renderer->gl.Enable(GL_BLEND);
gl.BlendEquationSeparate(colorOp, alphaOp); renderer->gl.BlendEquationSeparate(colorOp, alphaOp);
gl.BlendFuncSeparate(colorSrc, colorDst, alphaSrc, alphaDst); 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::Red),
((int)pass.blend.mask & (int)BlendMask::Green), ((int)pass.blend.mask & (int)BlendMask::Green),
((int)pass.blend.mask & (int)BlendMask::Blue), ((int)pass.blend.mask & (int)BlendMask::Blue),
@ -1351,7 +1355,7 @@ namespace Blah
unsigned char b = pass.blend.rgba >> 8; unsigned char b = pass.blend.rgba >> 8;
unsigned char a = pass.blend.rgba; unsigned char a = pass.blend.rgba;
gl.BlendColor( renderer->gl.BlendColor(
r / 255.0f, r / 255.0f,
g / 255.0f, g / 255.0f,
b / 255.0f, b / 255.0f,
@ -1362,38 +1366,38 @@ namespace Blah
{ {
if (pass.depth == Compare::None) if (pass.depth == Compare::None)
{ {
gl.Disable(GL_DEPTH_TEST); renderer->gl.Disable(GL_DEPTH_TEST);
} }
else else
{ {
gl.Enable(GL_DEPTH_TEST); renderer->gl.Enable(GL_DEPTH_TEST);
switch (pass.depth) switch (pass.depth)
{ {
case Compare::None: break; case Compare::None: break;
case Compare::Always: case Compare::Always:
gl.DepthFunc(GL_ALWAYS); renderer->gl.DepthFunc(GL_ALWAYS);
break; break;
case Compare::Equal: case Compare::Equal:
gl.DepthFunc(GL_EQUAL); renderer->gl.DepthFunc(GL_EQUAL);
break; break;
case Compare::Greater: case Compare::Greater:
gl.DepthFunc(GL_GREATER); renderer->gl.DepthFunc(GL_GREATER);
break; break;
case Compare::GreatorOrEqual: case Compare::GreatorOrEqual:
gl.DepthFunc(GL_GEQUAL); renderer->gl.DepthFunc(GL_GEQUAL);
break; break;
case Compare::Less: case Compare::Less:
gl.DepthFunc(GL_LESS); renderer->gl.DepthFunc(GL_LESS);
break; break;
case Compare::LessOrEqual: case Compare::LessOrEqual:
gl.DepthFunc(GL_LEQUAL); renderer->gl.DepthFunc(GL_LEQUAL);
break; break;
case Compare::Never: case Compare::Never:
gl.DepthFunc(GL_NEVER); renderer->gl.DepthFunc(GL_NEVER);
break; break;
case Compare::NotEqual: case Compare::NotEqual:
gl.DepthFunc(GL_NOTEQUAL); renderer->gl.DepthFunc(GL_NOTEQUAL);
break; break;
} }
} }
@ -1403,18 +1407,18 @@ namespace Blah
{ {
if (pass.cull == Cull::None) if (pass.cull == Cull::None)
{ {
gl.Disable(GL_CULL_FACE); renderer->gl.Disable(GL_CULL_FACE);
} }
else else
{ {
gl.Enable(GL_CULL_FACE); renderer->gl.Enable(GL_CULL_FACE);
if (pass.cull == Cull::Back) if (pass.cull == Cull::Back)
gl.CullFace(GL_BACK); renderer->gl.CullFace(GL_BACK);
else if (pass.cull == Cull::Front) else if (pass.cull == Cull::Front)
gl.CullFace(GL_FRONT); renderer->gl.CullFace(GL_FRONT);
else else
gl.CullFace(GL_FRONT_AND_BACK); renderer->gl.CullFace(GL_FRONT_AND_BACK);
} }
} }
@ -1423,14 +1427,14 @@ namespace Blah
Rectf viewport = pass.viewport; Rectf viewport = pass.viewport;
viewport.y = size.y - viewport.y - viewport.h; 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 // Scissor
{ {
if (!pass.has_scissor) if (!pass.has_scissor)
{ {
gl.Disable(GL_SCISSOR_TEST); renderer->gl.Disable(GL_SCISSOR_TEST);
} }
else else
{ {
@ -1442,21 +1446,21 @@ namespace Blah
if (scissor.h < 0) if (scissor.h < 0)
scissor.h = 0; scissor.h = 0;
gl.Enable(GL_SCISSOR_TEST); renderer->gl.Enable(GL_SCISSOR_TEST);
gl.Scissor((GLint)scissor.x, (GLint)scissor.y, (GLint)scissor.w, (GLint)scissor.h); renderer->gl.Scissor((GLint)scissor.x, (GLint)scissor.y, (GLint)scissor.w, (GLint)scissor.h);
} }
} }
// Draw the Mesh // Draw the Mesh
{ {
gl.BindVertexArray(mesh->gl_id()); renderer->gl.BindVertexArray(mesh->gl_id());
GLenum index_format = mesh->gl_index_format(); GLenum index_format = mesh->gl_index_format();
int index_size = mesh->gl_index_size(); int index_size = mesh->gl_index_size();
if (pass.instance_count > 0) if (pass.instance_count > 0)
{ {
gl.DrawElementsInstanced( renderer->gl.DrawElementsInstanced(
GL_TRIANGLES, GL_TRIANGLES,
(GLint)(pass.index_count), (GLint)(pass.index_count),
index_format, index_format,
@ -1465,18 +1469,18 @@ namespace Blah
} }
else else
{ {
gl.DrawElements( renderer->gl.DrawElements(
GL_TRIANGLES, GL_TRIANGLES,
(GLint)(pass.index_count), (GLint)(pass.index_count),
index_format, index_format,
(void*)(index_size * pass.index_start)); (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; int clear = 0;
@ -1487,14 +1491,28 @@ namespace Blah
if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil) if (((int)mask & (int)ClearMask::Stencil) == (int)ClearMask::Stencil)
clear |= GL_STENCIL_BUFFER_BIT; clear |= GL_STENCIL_BUFFER_BIT;
gl.BindFramebuffer(GL_FRAMEBUFFER, 0); renderer->gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
gl.Disable(GL_SCISSOR_TEST); renderer->gl.Disable(GL_SCISSOR_TEST);
gl.ColorMask(true, true, true, true); renderer->gl.ColorMask(true, true, true, true);
gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f); renderer->gl.ClearColor(color.r / 255.0f, color.g / 255.0f, color.b / 255.0f, color.a / 255.0f);
gl.ClearDepth(depth); renderer->gl.ClearDepth(depth);
gl.ClearStencil(stencil); renderer->gl.ClearStencil(stencil);
gl.Clear(clear); 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