prepare for software renderer
do not use or your Furnace will need a replacement
This commit is contained in:
parent
c527eaa946
commit
b6fcba2ba3
|
@ -769,6 +769,7 @@ extern/imgui_patched/imgui_draw.cpp
|
||||||
extern/imgui_patched/imgui_tables.cpp
|
extern/imgui_patched/imgui_tables.cpp
|
||||||
extern/imgui_patched/imgui_widgets.cpp
|
extern/imgui_patched/imgui_widgets.cpp
|
||||||
extern/imgui_patched/backends/imgui_impl_sdl2.cpp
|
extern/imgui_patched/backends/imgui_impl_sdl2.cpp
|
||||||
|
extern/imgui_software_renderer/imgui_sw.cpp
|
||||||
extern/imgui_patched/misc/cpp/imgui_stdlib.cpp
|
extern/imgui_patched/misc/cpp/imgui_stdlib.cpp
|
||||||
extern/igfd/ImGuiFileDialog.cpp
|
extern/igfd/ImGuiFileDialog.cpp
|
||||||
|
|
||||||
|
@ -776,6 +777,7 @@ src/gui/plot_nolerp.cpp
|
||||||
|
|
||||||
src/gui/render.cpp
|
src/gui/render.cpp
|
||||||
src/gui/render/abstract.cpp
|
src/gui/render/abstract.cpp
|
||||||
|
src/gui/render/renderSoftware.cpp
|
||||||
|
|
||||||
src/gui/font_exo.cpp
|
src/gui/font_exo.cpp
|
||||||
src/gui/font_liberationSans.cpp
|
src/gui/font_liberationSans.cpp
|
||||||
|
@ -1010,6 +1012,7 @@ if (BUILD_GUI)
|
||||||
list(APPEND DEPENDENCIES_INCLUDE_DIRS
|
list(APPEND DEPENDENCIES_INCLUDE_DIRS
|
||||||
extern/imgui_patched
|
extern/imgui_patched
|
||||||
extern/imgui_patched/backends
|
extern/imgui_patched/backends
|
||||||
|
extern/imgui_software_renderer
|
||||||
extern/igfd
|
extern/igfd
|
||||||
)
|
)
|
||||||
if (WIN32 OR APPLE)
|
if (WIN32 OR APPLE)
|
||||||
|
|
2
extern/imgui_software_renderer/.gitignore
vendored
Normal file
2
extern/imgui_software_renderer/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/build/*
|
||||||
|
*.bin
|
44
extern/imgui_software_renderer/README.md
vendored
Normal file
44
extern/imgui_software_renderer/README.md
vendored
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
# MODIFIED
|
||||||
|
|
||||||
|
this is a modified version of `https://github.com/JesusKrists/imgui_software_renderer`, which itself is a modified version of `https://github.com/emilk/imgui_software_renderer`.
|
||||||
|
|
||||||
|
# Dear ImGui software renderer
|
||||||
|
This is a software renderer for [Dear ImGui](https://github.com/ocornut/imgui).
|
||||||
|
I built it not out of a specific need, but because it was fun.
|
||||||
|
The goal was to get something accurate and decently fast in not too many lines of code.
|
||||||
|
It renders a complex GUI in 1-10 milliseconds on a modern laptop.
|
||||||
|
|
||||||
|
## What it is:
|
||||||
|
As the name implies, this is a software renderer for ImGui. It does not handle any windows or input. In the supplied example I use [SDL2](www.libsdl.org) for that.
|
||||||
|
|
||||||
|
## How to use it
|
||||||
|
Just copy `imgui_sw.hpp` and `imgui_sw.cpp`. There are no other dependencies beside Dear ImGui. Requires C++11.
|
||||||
|
|
||||||
|
## How to test it
|
||||||
|
```
|
||||||
|
git clone https://github.com/emilk/imgui_software_renderer.git
|
||||||
|
cd imgui_software_renderer
|
||||||
|
git submodule update --init --recursive
|
||||||
|
./build_and_run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
For the example to work you will need to have SDL2 on your system.
|
||||||
|
|
||||||
|
## Example:
|
||||||
|
This renders in 7 ms on my MacBook Pro:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Alternatives
|
||||||
|
There is another software rasterizer for ImGui (which I did not know about when I wrote mine) at https://github.com/sronsse/imgui/tree/sw_rasterizer_example/examples/sdl_sw_example.
|
||||||
|
I have not compared the two (yet).
|
||||||
|
|
||||||
|
## Future work:
|
||||||
|
* We do not yet support painting with any other texture than the default font texture.
|
||||||
|
* Optimize rendering of gradient rectangles (common for color pickers)
|
||||||
|
* Compare my software renderer to [the one by](https://github.com/sronsse/imgui/tree/sw_rasterizer_example/examples/sdl_sw_example) @sronsse
|
||||||
|
|
||||||
|
## License:
|
||||||
|
This software is dual-licensed to the public domain and under the following
|
||||||
|
license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
publish, and distribute this file as you see fit.
|
703
extern/imgui_software_renderer/imgui_sw.cpp
vendored
Normal file
703
extern/imgui_software_renderer/imgui_sw.cpp
vendored
Normal file
|
@ -0,0 +1,703 @@
|
||||||
|
// By Emil Ernerfeldt 2018
|
||||||
|
// LICENSE:
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
#include "imgui_sw.hpp"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <math.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
struct ImGui_ImplSW_Data
|
||||||
|
{
|
||||||
|
SDL_Window* Window;
|
||||||
|
SWTexture* FontTexture;
|
||||||
|
|
||||||
|
ImGui_ImplSW_Data() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SwOptions
|
||||||
|
{
|
||||||
|
bool optimize_text = true;// No reason to turn this off.
|
||||||
|
bool optimize_rectangles = true;// No reason to turn this off.
|
||||||
|
};
|
||||||
|
|
||||||
|
static ImGui_ImplSW_Data* ImGui_ImplSW_GetBackendData()
|
||||||
|
{
|
||||||
|
return ImGui::GetCurrentContext() ? (ImGui_ImplSW_Data*)ImGui::GetIO().BackendRendererUserData : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: de-namespace and static-ize
|
||||||
|
namespace {
|
||||||
|
struct PaintTarget
|
||||||
|
{
|
||||||
|
uint32_t *pixels;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
ImVec2 scale;// Multiply ImGui (point) coordinates with this to get pixel coordinates.
|
||||||
|
ImVec2 DisplayPos;
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
struct ColorInt
|
||||||
|
{
|
||||||
|
uint8_t r, g, b, a = 0;
|
||||||
|
ColorInt(const std::string &) {}
|
||||||
|
|
||||||
|
|
||||||
|
ColorInt &operator*=(const ColorInt &other)
|
||||||
|
{
|
||||||
|
r = r * other.r / 255;
|
||||||
|
g = g * other.g / 255;
|
||||||
|
b = b * other.b / 255;
|
||||||
|
a = a * other.a / 255;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
uint32_t blend(const ColorInt &target, const ColorInt &source)
|
||||||
|
{
|
||||||
|
if (source.a >= 255) return *reinterpret_cast<const uint32_t *>(&source);
|
||||||
|
return (target.a << 24u) | (((source.b * source.a + target.b * (255 - source.a)) / 255) << 16u)
|
||||||
|
| (((source.g * source.a + target.g * (255 - source.a)) / 255) << 8u)
|
||||||
|
| ((source.r * source.a + target.r * (255 - source.a)) / 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Used for interpolating vertex attributes (color and texture coordinates) in a triangle.
|
||||||
|
|
||||||
|
struct Barycentric
|
||||||
|
{
|
||||||
|
float w0, w1, w2;
|
||||||
|
};
|
||||||
|
|
||||||
|
Barycentric operator*(const float f, const Barycentric &va) { return { f * va.w0, f * va.w1, f * va.w2 }; }
|
||||||
|
|
||||||
|
void operator+=(Barycentric &a, const Barycentric &b)
|
||||||
|
{
|
||||||
|
a.w0 += b.w0;
|
||||||
|
a.w1 += b.w1;
|
||||||
|
a.w2 += b.w2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Barycentric operator+(const Barycentric &a, const Barycentric &b)
|
||||||
|
{
|
||||||
|
return Barycentric{ a.w0 + b.w0, a.w1 + b.w1, a.w2 + b.w2 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Useful operators on ImGui vectors:
|
||||||
|
|
||||||
|
ImVec2 operator*(const float f, const ImVec2 &v) { return ImVec2{ f * v.x, f * v.y }; }
|
||||||
|
|
||||||
|
bool operator!=(const ImVec2 &a, const ImVec2 &b) { return a.x != b.x || a.y != b.y; }
|
||||||
|
|
||||||
|
ImVec4 operator*(const float f, const ImVec4 &v) { return ImVec4{ f * v.x, f * v.y, f * v.z, f * v.w }; }
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// Copies of functions in ImGui, inlined for speed:
|
||||||
|
|
||||||
|
ImVec4 color_convert_u32_to_float4(ImU32 in)
|
||||||
|
{
|
||||||
|
const float s = 1.0f / 255.0f;
|
||||||
|
return ImVec4(((in >> IM_COL32_R_SHIFT) & 0xFF) * s,
|
||||||
|
((in >> IM_COL32_G_SHIFT) & 0xFF) * s,
|
||||||
|
((in >> IM_COL32_B_SHIFT) & 0xFF) * s,
|
||||||
|
((in >> IM_COL32_A_SHIFT) & 0xFF) * s);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImU32 color_convert_float4_to_u32(const ImVec4 &in)
|
||||||
|
{
|
||||||
|
ImU32 out;
|
||||||
|
out = uint32_t(in.x * 255.0f + 0.5f) << IM_COL32_R_SHIFT;
|
||||||
|
out |= uint32_t(in.y * 255.0f + 0.5f) << IM_COL32_G_SHIFT;
|
||||||
|
out |= uint32_t(in.z * 255.0f + 0.5f) << IM_COL32_B_SHIFT;
|
||||||
|
out |= uint32_t(in.w * 255.0f + 0.5f) << IM_COL32_A_SHIFT;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// For fast and subpixel-perfect triangle rendering we used fixed point arithmetic.
|
||||||
|
// To keep the code simple we use 64 bits to avoid overflows.
|
||||||
|
// TODO: make it 32-bit or else
|
||||||
|
|
||||||
|
using Int = int64_t;
|
||||||
|
const Int kFixedBias = 256;
|
||||||
|
|
||||||
|
struct Point
|
||||||
|
{
|
||||||
|
Int x, y;
|
||||||
|
};
|
||||||
|
|
||||||
|
Int orient2d(const Point &a, const Point &b, const Point &c)
|
||||||
|
{
|
||||||
|
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
Int as_int(float v) { return static_cast<Int>(floor(v * kFixedBias)); }
|
||||||
|
|
||||||
|
Point as_point(ImVec2 v) { return Point{ as_int(v.x), as_int(v.y) }; }
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
float min3(float a, float b, float c)
|
||||||
|
{
|
||||||
|
if (a < b && a < c) { return a; }
|
||||||
|
return b < c ? b : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
float max3(float a, float b, float c)
|
||||||
|
{
|
||||||
|
if (a > b && a > c) { return a; }
|
||||||
|
return b > c ? b : c;
|
||||||
|
}
|
||||||
|
|
||||||
|
float barycentric(const ImVec2 &a, const ImVec2 &b, const ImVec2 &point)
|
||||||
|
{
|
||||||
|
return (b.x - a.x) * (point.y - a.y) - (b.y - a.y) * (point.x - a.x);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint8_t sample_font_texture(const SWTexture &texture, int x, int y)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<const uint8_t *>(texture.pixels)[x + y * texture.width];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint32_t sample_texture(const SWTexture &texture, int x, int y) { return texture.pixels[x + y * texture.width]; }
|
||||||
|
|
||||||
|
void paint_uniform_rectangle(const PaintTarget &target,
|
||||||
|
const ImVec2 &min_f,
|
||||||
|
const ImVec2 &max_f,
|
||||||
|
const ColorInt &color)
|
||||||
|
{
|
||||||
|
// Integer bounding box [min, max):
|
||||||
|
int min_x_i = static_cast<int>(target.scale.x * min_f.x + 0.5f);
|
||||||
|
int min_y_i = static_cast<int>(target.scale.y * min_f.y + 0.5f);
|
||||||
|
int max_x_i = static_cast<int>(target.scale.x * max_f.x + 0.5f);
|
||||||
|
int max_y_i = static_cast<int>(target.scale.y * max_f.y + 0.5f);
|
||||||
|
|
||||||
|
// Clamp to render target:
|
||||||
|
min_x_i = std::max(min_x_i, 0);
|
||||||
|
min_y_i = std::max(min_y_i, 0);
|
||||||
|
max_x_i = std::min(max_x_i, target.width);
|
||||||
|
max_y_i = std::min(max_y_i, target.height);
|
||||||
|
|
||||||
|
// We often blend the same colors over and over again, so optimize for this (saves 25% total cpu):
|
||||||
|
uint32_t last_target_pixel = target.pixels[min_y_i * target.width + min_x_i];
|
||||||
|
const auto *lastColorRef = reinterpret_cast<const ColorInt *>(&last_target_pixel);
|
||||||
|
uint32_t last_output = blend(*lastColorRef, color);
|
||||||
|
|
||||||
|
for (int y = min_y_i; y < max_y_i; ++y) {
|
||||||
|
for (int x = min_x_i; x < max_x_i; ++x) {
|
||||||
|
uint32_t &target_pixel = target.pixels[y * target.width + x];
|
||||||
|
if (target_pixel == last_target_pixel) {
|
||||||
|
target_pixel = last_output;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
last_target_pixel = target_pixel;
|
||||||
|
const auto *colorRef = reinterpret_cast<const ColorInt *>(&target_pixel);
|
||||||
|
target_pixel = blend(*colorRef, color);
|
||||||
|
last_output = target_pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint_uniform_textured_rectangle(const PaintTarget &target,
|
||||||
|
const SWTexture &texture,
|
||||||
|
const ImVec4 &clip_rect,
|
||||||
|
const ImDrawVert &min_v,
|
||||||
|
const ImDrawVert &max_v)
|
||||||
|
{
|
||||||
|
const ImVec2 min_p = ImVec2(target.scale.x * min_v.pos.x, target.scale.y * min_v.pos.y);
|
||||||
|
const ImVec2 max_p = ImVec2(target.scale.x * max_v.pos.x, target.scale.y * max_v.pos.y);
|
||||||
|
|
||||||
|
float distanceX = max_p.x - min_p.x;
|
||||||
|
float distanceY = max_p.y - min_p.y;
|
||||||
|
if (distanceX == 0 || distanceY == 0) { return; }
|
||||||
|
|
||||||
|
// Find bounding box:
|
||||||
|
float min_x_f = min_p.x;
|
||||||
|
float min_y_f = min_p.y;
|
||||||
|
float max_x_f = max_p.x;
|
||||||
|
float max_y_f = max_p.y;
|
||||||
|
|
||||||
|
// Clip against clip_rect:
|
||||||
|
min_x_f = std::max(min_x_f, target.scale.x * clip_rect.x - target.DisplayPos.x);
|
||||||
|
min_y_f = std::max(min_y_f, target.scale.y * clip_rect.y - target.DisplayPos.y);
|
||||||
|
max_x_f = std::min(max_x_f, target.scale.x * clip_rect.z - 0.5f - target.DisplayPos.x);
|
||||||
|
max_y_f = std::min(max_y_f, target.scale.y * clip_rect.w - 0.5f - target.DisplayPos.y);
|
||||||
|
|
||||||
|
// Integer bounding box [min, max):
|
||||||
|
int min_x_i = static_cast<int>(min_x_f);
|
||||||
|
int min_y_i = static_cast<int>(min_y_f);
|
||||||
|
int max_x_i = static_cast<int>(max_x_f + 1.0f);
|
||||||
|
int max_y_i = static_cast<int>(max_y_f + 1.0f);
|
||||||
|
|
||||||
|
// Clip against render target:
|
||||||
|
min_x_i = std::max(min_x_i, 0);
|
||||||
|
min_y_i = std::max(min_y_i, 0);
|
||||||
|
max_x_i = std::min(max_x_i, target.width);
|
||||||
|
max_y_i = std::min(max_y_i, target.height);
|
||||||
|
|
||||||
|
const auto topleft = ImVec2(min_x_i + 0.5f * target.scale.x, min_y_i + 0.5f * target.scale.y);
|
||||||
|
const ImVec2 delta_uv_per_pixel = {
|
||||||
|
(max_v.uv.x - min_v.uv.x) / distanceX,
|
||||||
|
(max_v.uv.y - min_v.uv.y) / distanceY,
|
||||||
|
};
|
||||||
|
const ImVec2 uv_topleft = {
|
||||||
|
min_v.uv.x + (topleft.x - min_v.pos.x) * delta_uv_per_pixel.x,
|
||||||
|
min_v.uv.y + (topleft.y - min_v.pos.y) * delta_uv_per_pixel.y,
|
||||||
|
};
|
||||||
|
|
||||||
|
int startX = uv_topleft.x * (texture.width - 1.0f) + 0.5f;
|
||||||
|
int startY = uv_topleft.y * (texture.height - 1.0f) + 0.5f;
|
||||||
|
|
||||||
|
int currentX = startX;
|
||||||
|
int currentY = startY;
|
||||||
|
|
||||||
|
float deltaX = delta_uv_per_pixel.x * texture.width;
|
||||||
|
float deltaY = delta_uv_per_pixel.y * texture.height;
|
||||||
|
|
||||||
|
for (int y = min_y_i; y < max_y_i; ++y) {
|
||||||
|
currentX = startX;
|
||||||
|
for (int x = min_x_i; x < max_x_i; ++x) {
|
||||||
|
uint32_t &target_pixel = target.pixels[y * target.width + x];
|
||||||
|
const auto *targetColorRef = reinterpret_cast<const ColorInt *>(&target_pixel);
|
||||||
|
const auto *colorRef = reinterpret_cast<const ColorInt *>(&min_v.col);
|
||||||
|
|
||||||
|
if (texture.isAlpha) {
|
||||||
|
uint8_t texel = sample_font_texture(texture, currentX, currentY);
|
||||||
|
if (deltaX != 0 && currentX < texture.width - 1) { currentX += 1; }
|
||||||
|
|
||||||
|
// The font texture is all black or all white, so optimize for this:
|
||||||
|
if (texel == 0) { continue; }
|
||||||
|
if (texel == 255) {
|
||||||
|
target_pixel = blend(*targetColorRef, *colorRef);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
auto texColor = sample_texture(texture, currentX, currentY);
|
||||||
|
auto src_color = reinterpret_cast<ColorInt *>(&texColor);
|
||||||
|
|
||||||
|
if (deltaX != 0 && currentX < texture.width - 1) { currentX += 1; }
|
||||||
|
|
||||||
|
*src_color *= *colorRef;
|
||||||
|
target_pixel = blend(*targetColorRef, *src_color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (deltaY != 0 && currentY < texture.height - 1) { currentY += 1; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When two triangles share an edge, we want to draw the pixels on that edge exactly once.
|
||||||
|
// The edge will be the same, but the direction will be the opposite
|
||||||
|
// (assuming the two triangles have the same winding order).
|
||||||
|
// Which edge wins? This functions decides.
|
||||||
|
bool is_dominant_edge(ImVec2 edge)
|
||||||
|
{
|
||||||
|
// return edge.x < 0 || (edge.x == 0 && edge.y > 0);
|
||||||
|
return edge.y > 0 || (edge.y == 0 && edge.x < 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles triangles in any winding order (CW/CCW)
|
||||||
|
void paint_triangle(const PaintTarget &target,
|
||||||
|
const SWTexture *texture,
|
||||||
|
const ImVec4 &clip_rect,
|
||||||
|
const ImDrawVert &v0,
|
||||||
|
const ImDrawVert &v1,
|
||||||
|
const ImDrawVert &v2)
|
||||||
|
{
|
||||||
|
const ImVec2 p0 = ImVec2(target.scale.x * v0.pos.x, target.scale.y * v0.pos.y);
|
||||||
|
const ImVec2 p1 = ImVec2(target.scale.x * v1.pos.x, target.scale.y * v1.pos.y);
|
||||||
|
const ImVec2 p2 = ImVec2(target.scale.x * v2.pos.x, target.scale.y * v2.pos.y);
|
||||||
|
|
||||||
|
const auto rect_area = barycentric(p0, p1, p2);// Can be positive or negative depending on winding order
|
||||||
|
if (rect_area == 0.0f) { return; }
|
||||||
|
// if (rect_area < 0.0f) { return paint_triangle(target, texture, clip_rect, v0, v2, v1); }
|
||||||
|
|
||||||
|
// Find bounding box:
|
||||||
|
float min_x_f = min3(p0.x, p1.x, p2.x);
|
||||||
|
float min_y_f = min3(p0.y, p1.y, p2.y);
|
||||||
|
float max_x_f = max3(p0.x, p1.x, p2.x);
|
||||||
|
float max_y_f = max3(p0.y, p1.y, p2.y);
|
||||||
|
|
||||||
|
// Clip against clip_rect:
|
||||||
|
min_x_f = std::max(min_x_f, target.scale.x * clip_rect.x - target.DisplayPos.x);
|
||||||
|
min_y_f = std::max(min_y_f, target.scale.y * clip_rect.y - target.DisplayPos.y);
|
||||||
|
max_x_f = std::min(max_x_f, target.scale.x * clip_rect.z - 0.5f - target.DisplayPos.x);
|
||||||
|
max_y_f = std::min(max_y_f, target.scale.y * clip_rect.w - 0.5f - target.DisplayPos.y);
|
||||||
|
|
||||||
|
// Integer bounding box [min, max):
|
||||||
|
int min_x_i = static_cast<int>(min_x_f);
|
||||||
|
int min_y_i = static_cast<int>(min_y_f);
|
||||||
|
int max_x_i = static_cast<int>(max_x_f + 1.0f);
|
||||||
|
int max_y_i = static_cast<int>(max_y_f + 1.0f);
|
||||||
|
|
||||||
|
// Clip against render target:
|
||||||
|
min_x_i = std::max(min_x_i, 0);
|
||||||
|
min_y_i = std::max(min_y_i, 0);
|
||||||
|
max_x_i = std::min(max_x_i, target.width);
|
||||||
|
max_y_i = std::min(max_y_i, target.height);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// Set up interpolation of barycentric coordinates:
|
||||||
|
|
||||||
|
const auto topleft = ImVec2(min_x_i + 0.5f * target.scale.x, min_y_i + 0.5f * target.scale.y);
|
||||||
|
const auto dx = ImVec2(1, 0);
|
||||||
|
const auto dy = ImVec2(0, 1);
|
||||||
|
|
||||||
|
const auto w0_topleft = barycentric(p1, p2, topleft);
|
||||||
|
const auto w1_topleft = barycentric(p2, p0, topleft);
|
||||||
|
const auto w2_topleft = barycentric(p0, p1, topleft);
|
||||||
|
|
||||||
|
const auto w0_dx = barycentric(p1, p2, topleft + dx) - w0_topleft;
|
||||||
|
const auto w1_dx = barycentric(p2, p0, topleft + dx) - w1_topleft;
|
||||||
|
const auto w2_dx = barycentric(p0, p1, topleft + dx) - w2_topleft;
|
||||||
|
|
||||||
|
const auto w0_dy = barycentric(p1, p2, topleft + dy) - w0_topleft;
|
||||||
|
const auto w1_dy = barycentric(p2, p0, topleft + dy) - w1_topleft;
|
||||||
|
const auto w2_dy = barycentric(p0, p1, topleft + dy) - w2_topleft;
|
||||||
|
|
||||||
|
const Barycentric bary_0{ 1, 0, 0 };
|
||||||
|
const Barycentric bary_1{ 0, 1, 0 };
|
||||||
|
const Barycentric bary_2{ 0, 0, 1 };
|
||||||
|
|
||||||
|
const auto inv_area = 1 / rect_area;
|
||||||
|
const Barycentric bary_topleft = inv_area * (w0_topleft * bary_0 + w1_topleft * bary_1 + w2_topleft * bary_2);
|
||||||
|
const Barycentric bary_dx = inv_area * (w0_dx * bary_0 + w1_dx * bary_1 + w2_dx * bary_2);
|
||||||
|
const Barycentric bary_dy = inv_area * (w0_dy * bary_0 + w1_dy * bary_1 + w2_dy * bary_2);
|
||||||
|
|
||||||
|
Barycentric bary_current_row = bary_topleft;
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// For pixel-perfect inside/outside testing:
|
||||||
|
|
||||||
|
const int sign = rect_area > 0 ? 1 : -1;// winding order?
|
||||||
|
|
||||||
|
const int bias0i = is_dominant_edge(p2 - p1) ? 0 : -1;
|
||||||
|
const int bias1i = is_dominant_edge(p0 - p2) ? 0 : -1;
|
||||||
|
const int bias2i = is_dominant_edge(p1 - p0) ? 0 : -1;
|
||||||
|
|
||||||
|
const auto p0i = as_point(p0);
|
||||||
|
const auto p1i = as_point(p1);
|
||||||
|
const auto p2i = as_point(p2);
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const bool has_uniform_color = (v0.col == v1.col && v0.col == v2.col);
|
||||||
|
|
||||||
|
const ImVec4 c0 = color_convert_u32_to_float4(v0.col);
|
||||||
|
const ImVec4 c1 = color_convert_u32_to_float4(v1.col);
|
||||||
|
const ImVec4 c2 = color_convert_u32_to_float4(v2.col);
|
||||||
|
|
||||||
|
// We often blend the same colors over and over again, so optimize for this (saves 10% total cpu):
|
||||||
|
uint32_t last_target_pixel = 0;
|
||||||
|
const auto *lastColorRef = reinterpret_cast<const ColorInt *>(&last_target_pixel);
|
||||||
|
const auto *colorRef = reinterpret_cast<const ColorInt *>(&v0.col);
|
||||||
|
uint32_t last_output = blend(*lastColorRef, *colorRef);
|
||||||
|
|
||||||
|
for (int y = min_y_i; y < max_y_i; ++y) {
|
||||||
|
auto bary = bary_current_row;
|
||||||
|
|
||||||
|
bool has_been_inside_this_row = false;
|
||||||
|
|
||||||
|
for (int x = min_x_i; x < max_x_i; ++x) {
|
||||||
|
const auto w0 = bary.w0;
|
||||||
|
const auto w1 = bary.w1;
|
||||||
|
const auto w2 = bary.w2;
|
||||||
|
bary += bary_dx;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Inside/outside test:
|
||||||
|
const auto p = Point{ kFixedBias * x + kFixedBias / 2, kFixedBias * y + kFixedBias / 2 };
|
||||||
|
const auto w0i = sign * orient2d(p1i, p2i, p) + bias0i;
|
||||||
|
const auto w1i = sign * orient2d(p2i, p0i, p) + bias1i;
|
||||||
|
const auto w2i = sign * orient2d(p0i, p1i, p) + bias2i;
|
||||||
|
if (w0i < 0 || w1i < 0 || w2i < 0) {
|
||||||
|
if (has_been_inside_this_row) {
|
||||||
|
break;// Gives a nice 10% speedup
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
has_been_inside_this_row = true;
|
||||||
|
|
||||||
|
uint32_t &target_pixel = target.pixels[y * target.width + x];
|
||||||
|
|
||||||
|
if (has_uniform_color && !texture) {
|
||||||
|
if (target_pixel == last_target_pixel) {
|
||||||
|
target_pixel = last_output;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
last_target_pixel = target_pixel;
|
||||||
|
target_pixel = blend(*lastColorRef, *colorRef);
|
||||||
|
last_output = target_pixel;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec4 src_color;
|
||||||
|
|
||||||
|
if (has_uniform_color) {
|
||||||
|
src_color = c0;
|
||||||
|
} else {
|
||||||
|
src_color = w0 * c0 + w1 * c1 + w2 * c2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texture) {
|
||||||
|
if (!texture->isAlpha) { printf("warning: different texture\n"); }
|
||||||
|
|
||||||
|
const ImVec2 uv = w0 * v0.uv + w1 * v1.uv + w2 * v2.uv;
|
||||||
|
int x = uv.x * (texture->width - 1.0f) + 0.5f;
|
||||||
|
int y = uv.y * (texture->height - 1.0f) + 0.5f;
|
||||||
|
src_color.w *= sample_font_texture(*texture, x, y) / 255.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src_color.w <= 0.0f) { continue; }// Transparent.
|
||||||
|
if (src_color.w >= 1.0f) {
|
||||||
|
// Opaque, no blending needed:
|
||||||
|
target_pixel = color_convert_float4_to_u32(src_color);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec4 target_color = color_convert_u32_to_float4(target_pixel);
|
||||||
|
const auto blended_color = src_color.w * src_color + (1.0f - src_color.w) * target_color;
|
||||||
|
target_pixel = color_convert_float4_to_u32(blended_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
bary_current_row += bary_dy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint_draw_cmd(const PaintTarget &target,
|
||||||
|
const ImDrawVert *vertices,
|
||||||
|
const ImDrawIdx *idx_buffer,
|
||||||
|
const ImDrawCmd &pcmd,
|
||||||
|
const SwOptions &options)
|
||||||
|
{
|
||||||
|
const auto texture = reinterpret_cast<const SWTexture *>(pcmd.TextureId);
|
||||||
|
IM_ASSERT(texture);
|
||||||
|
|
||||||
|
// ImGui uses the first pixel for "white".
|
||||||
|
const ImVec2 white_uv = ImVec2(0.5f / texture->width, 0.5f / texture->height);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i + 3 <= pcmd.ElemCount;) {
|
||||||
|
ImDrawVert v0 = vertices[idx_buffer[i + 0]];
|
||||||
|
v0.pos.x -= target.DisplayPos.x;
|
||||||
|
v0.pos.y -= target.DisplayPos.y;
|
||||||
|
ImDrawVert v1 = vertices[idx_buffer[i + 1]];
|
||||||
|
v1.pos.x -= target.DisplayPos.x;
|
||||||
|
v1.pos.y -= target.DisplayPos.y;
|
||||||
|
ImDrawVert v2 = vertices[idx_buffer[i + 2]];
|
||||||
|
v2.pos.x -= target.DisplayPos.x;
|
||||||
|
v2.pos.y -= target.DisplayPos.y;
|
||||||
|
|
||||||
|
// Text is common, and is made of textured rectangles. So let's optimize for it.
|
||||||
|
// This assumes the ImGui way to layout text does not change.
|
||||||
|
if (options.optimize_text && i + 6 <= pcmd.ElemCount && idx_buffer[i + 3] == idx_buffer[i + 0]
|
||||||
|
&& idx_buffer[i + 4] == idx_buffer[i + 2]) {
|
||||||
|
ImDrawVert v3 = vertices[idx_buffer[i + 5]];
|
||||||
|
v3.pos.x -= target.DisplayPos.x;
|
||||||
|
v3.pos.y -= target.DisplayPos.y;
|
||||||
|
|
||||||
|
if (v0.pos.x == v3.pos.x && v1.pos.x == v2.pos.x && v0.pos.y == v1.pos.y && v2.pos.y == v3.pos.y
|
||||||
|
&& v0.uv.x == v3.uv.x && v1.uv.x == v2.uv.x && v0.uv.y == v1.uv.y && v2.uv.y == v3.uv.y) {
|
||||||
|
const bool has_uniform_color = v0.col == v1.col && v0.col == v2.col && v0.col == v3.col;
|
||||||
|
|
||||||
|
const bool has_texture = v0.uv != white_uv || v1.uv != white_uv || v2.uv != white_uv || v3.uv != white_uv;
|
||||||
|
|
||||||
|
if (has_uniform_color && has_texture) {
|
||||||
|
paint_uniform_textured_rectangle(target, *texture, pcmd.ClipRect, v0, v2);
|
||||||
|
i += 6;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A lot of the big stuff are uniformly colored rectangles,
|
||||||
|
// so we can save a lot of CPU by detecting them:
|
||||||
|
if (options.optimize_rectangles && i + 6 <= pcmd.ElemCount) {
|
||||||
|
ImDrawVert v3 = vertices[idx_buffer[i + 3]];
|
||||||
|
v3.pos.x -= target.DisplayPos.x;
|
||||||
|
v3.pos.y -= target.DisplayPos.y;
|
||||||
|
ImDrawVert v4 = vertices[idx_buffer[i + 4]];
|
||||||
|
v4.pos.x -= target.DisplayPos.x;
|
||||||
|
v4.pos.y -= target.DisplayPos.y;
|
||||||
|
ImDrawVert v5 = vertices[idx_buffer[i + 5]];
|
||||||
|
v5.pos.x -= target.DisplayPos.x;
|
||||||
|
v5.pos.y -= target.DisplayPos.y;
|
||||||
|
|
||||||
|
ImVec2 min, max;
|
||||||
|
min.x = min3(v0.pos.x - target.DisplayPos.x, v1.pos.x - target.DisplayPos.x, v2.pos.x - target.DisplayPos.x);
|
||||||
|
min.y = min3(v0.pos.y - target.DisplayPos.y, v1.pos.y - target.DisplayPos.y, v2.pos.y - target.DisplayPos.y);
|
||||||
|
max.x = max3(v0.pos.x - target.DisplayPos.x, v1.pos.x - target.DisplayPos.x, v2.pos.x - target.DisplayPos.x);
|
||||||
|
max.y = max3(v0.pos.y - target.DisplayPos.y, v1.pos.y - target.DisplayPos.y, v2.pos.y - target.DisplayPos.y);
|
||||||
|
|
||||||
|
// Not the prettiest way to do this, but it catches all cases
|
||||||
|
// of a rectangle split into two triangles.
|
||||||
|
// TODO: Stop it from also assuming duplicate triangles is one rectangle.
|
||||||
|
if ((v0.pos.x == min.x || v0.pos.x == max.x) && (v0.pos.y == min.y || v0.pos.y == max.y)
|
||||||
|
&& (v1.pos.x == min.x || v1.pos.x == max.x) && (v1.pos.y == min.y || v1.pos.y == max.y)
|
||||||
|
&& (v2.pos.x == min.x || v2.pos.x == max.x) && (v2.pos.y == min.y || v2.pos.y == max.y)
|
||||||
|
&& (v3.pos.x == min.x || v3.pos.x == max.x) && (v3.pos.y == min.y || v3.pos.y == max.y)
|
||||||
|
&& (v4.pos.x == min.x || v4.pos.x == max.x) && (v4.pos.y == min.y || v4.pos.y == max.y)
|
||||||
|
&& (v5.pos.x == min.x || v5.pos.x == max.x) && (v5.pos.y == min.y || v5.pos.y == max.y)) {
|
||||||
|
const bool has_uniform_color =
|
||||||
|
v0.col == v1.col && v0.col == v2.col && v0.col == v3.col && v0.col == v4.col && v0.col == v5.col;
|
||||||
|
|
||||||
|
min.x = std::max(min.x, pcmd.ClipRect.x - target.DisplayPos.x);
|
||||||
|
min.y = std::max(min.y, pcmd.ClipRect.y - target.DisplayPos.y);
|
||||||
|
max.x = std::min(max.x, pcmd.ClipRect.z - 0.5f - target.DisplayPos.x);
|
||||||
|
max.y = std::min(max.y, pcmd.ClipRect.w - 0.5f - target.DisplayPos.y);
|
||||||
|
|
||||||
|
if (max.x < min.x || max.y < min.y) {
|
||||||
|
i += 6;
|
||||||
|
continue;
|
||||||
|
}// Completely clipped
|
||||||
|
|
||||||
|
if (has_uniform_color) {
|
||||||
|
const auto *colorRef = reinterpret_cast<const ColorInt *>(&v0.col);
|
||||||
|
paint_uniform_rectangle(target, min, max, *colorRef);
|
||||||
|
i += 6;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool has_texture = (v0.uv != white_uv || v1.uv != white_uv || v2.uv != white_uv);
|
||||||
|
paint_triangle(target, has_texture ? texture : nullptr, pcmd.ClipRect, v0, v1, v2);
|
||||||
|
i += 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint_draw_list(const PaintTarget &target, const ImDrawList *cmd_list, const SwOptions &options)
|
||||||
|
{
|
||||||
|
const ImDrawIdx *idx_buffer = &cmd_list->IdxBuffer[0];
|
||||||
|
const ImDrawVert *vertices = cmd_list->VtxBuffer.Data;
|
||||||
|
|
||||||
|
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.size(); cmd_i++) {
|
||||||
|
const ImDrawCmd &pcmd = cmd_list->CmdBuffer[cmd_i];
|
||||||
|
if (pcmd.UserCallback) {
|
||||||
|
pcmd.UserCallback(cmd_list, &pcmd);
|
||||||
|
} else {
|
||||||
|
paint_draw_cmd(target, vertices, idx_buffer, pcmd, options);
|
||||||
|
}
|
||||||
|
idx_buffer += pcmd.ElemCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace
|
||||||
|
|
||||||
|
void paint_imgui(uint32_t *pixels, ImDrawData *drawData, const SwOptions &options = {})
|
||||||
|
{
|
||||||
|
int fb_width = (int)(drawData->DisplaySize.x * drawData->FramebufferScale.x);
|
||||||
|
int fb_height = (int)(drawData->DisplaySize.y * drawData->FramebufferScale.y);
|
||||||
|
if (fb_width <= 0 || fb_height <= 0) return;
|
||||||
|
|
||||||
|
PaintTarget target{ pixels, fb_width, fb_height, drawData->FramebufferScale, drawData->DisplayPos };
|
||||||
|
|
||||||
|
for (int i = 0; i < drawData->CmdListsCount; ++i) {
|
||||||
|
paint_draw_list(target, drawData->CmdLists[i], options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NEW STUFF
|
||||||
|
|
||||||
|
bool ImGui_ImplSW_Init(SDL_Window* win) {
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
IM_ASSERT(io.BackendRendererUserData == nullptr);
|
||||||
|
|
||||||
|
if (SDL_HasWindowSurface(win)==SDL_FALSE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui_ImplSW_Data* bd = IM_NEW(ImGui_ImplSW_Data)();
|
||||||
|
bd->Window = win;
|
||||||
|
io.BackendRendererUserData = (void*)bd;
|
||||||
|
io.BackendRendererName = "imgui_sw";
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSW_Shutdown() {
|
||||||
|
ImGui_ImplSW_Data* bd = ImGui_ImplSW_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr);
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
ImGui_ImplSW_DestroyDeviceObjects();
|
||||||
|
io.BackendRendererName = nullptr;
|
||||||
|
io.BackendRendererUserData = nullptr;
|
||||||
|
IM_DELETE(bd);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSW_NewFrame() {
|
||||||
|
ImGui_ImplSW_Data* bd = ImGui_ImplSW_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr);
|
||||||
|
|
||||||
|
if (!bd->FontTexture) ImGui_ImplSW_CreateDeviceObjects();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSW_RenderDrawData(ImDrawData* draw_data) {
|
||||||
|
ImGui_ImplSW_Data* bd = ImGui_ImplSW_GetBackendData();
|
||||||
|
IM_ASSERT(bd != nullptr);
|
||||||
|
|
||||||
|
SDL_Surface* surf = SDL_GetWindowSurface(bd->Window);
|
||||||
|
if (!surf) return;
|
||||||
|
|
||||||
|
bool mustLock=SDL_MUSTLOCK(surf);
|
||||||
|
if (mustLock) {
|
||||||
|
if (SDL_LockSurface(surf)!=0) return;
|
||||||
|
}
|
||||||
|
paint_imgui((uint32_t*)surf->pixels,draw_data);
|
||||||
|
if (mustLock) {
|
||||||
|
SDL_UnlockSurface(surf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CREATE OBJECTS
|
||||||
|
|
||||||
|
bool ImGui_ImplSW_CreateFontsTexture() {
|
||||||
|
ImGuiIO &io = ImGui::GetIO();
|
||||||
|
ImGui_ImplSW_Data* bd = ImGui_ImplSW_GetBackendData();
|
||||||
|
|
||||||
|
// Load default font (embedded in code):
|
||||||
|
uint8_t *tex_data;
|
||||||
|
int font_width, font_height;
|
||||||
|
io.Fonts->GetTexDataAsAlpha8(&tex_data, &font_width, &font_height);
|
||||||
|
SWTexture* texture = new SWTexture((uint32_t*)tex_data,font_width,font_height,true);
|
||||||
|
io.Fonts->SetTexID(texture);
|
||||||
|
bd->FontTexture = texture;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSW_DestroyFontsTexture() {
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
ImGui_ImplSW_Data* bd = ImGui_ImplSW_GetBackendData();
|
||||||
|
if (bd->FontTexture)
|
||||||
|
{
|
||||||
|
delete bd->FontTexture;
|
||||||
|
io.Fonts->SetTexID(0);
|
||||||
|
bd->FontTexture = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui_ImplSW_CreateDeviceObjects() {
|
||||||
|
return ImGui_ImplSW_CreateFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGui_ImplSW_DestroyDeviceObjects() {
|
||||||
|
ImGui_ImplSW_DestroyFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
58
extern/imgui_software_renderer/imgui_sw.hpp
vendored
Normal file
58
extern/imgui_software_renderer/imgui_sw.hpp
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
// By Emil Ernerfeldt 2018
|
||||||
|
// LICENSE:
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
// WHAT:
|
||||||
|
// This is a software renderer for Dear ImGui.
|
||||||
|
// It is decently fast, but has a lot of room for optimization.
|
||||||
|
// The goal was to get something fast and decently accurate in not too many lines of code.
|
||||||
|
// LIMITATIONS:
|
||||||
|
// * It is not pixel-perfect, but it is good enough for must use cases.
|
||||||
|
// * It does not support painting with any other texture than the default font texture.
|
||||||
|
#pragma once
|
||||||
|
#include "imgui.h" // IMGUI_IMPL_API
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
struct SDL_Window;
|
||||||
|
struct ImDrawData;
|
||||||
|
|
||||||
|
struct SWTexture
|
||||||
|
{
|
||||||
|
uint32_t* pixels;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
bool managed, isAlpha;
|
||||||
|
|
||||||
|
SWTexture(uint32_t* pix, int w, int h, bool a=false):
|
||||||
|
pixels(pix),
|
||||||
|
width(w),
|
||||||
|
height(h),
|
||||||
|
managed(false),
|
||||||
|
isAlpha(a) {}
|
||||||
|
SWTexture(int w, int h, bool a=false):
|
||||||
|
width(w),
|
||||||
|
height(h),
|
||||||
|
managed(true),
|
||||||
|
isAlpha(a) {
|
||||||
|
pixels=new uint32_t[width*height];
|
||||||
|
}
|
||||||
|
~SWTexture() {
|
||||||
|
if (managed) delete[] pixels;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSW_Init(SDL_Window* win);
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSW_Shutdown();
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSW_NewFrame();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSW_RenderDrawData(ImDrawData* draw_data);
|
||||||
|
|
||||||
|
// Called by Init/NewFrame/Shutdown
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSW_CreateFontsTexture();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSW_DestroyFontsTexture();
|
||||||
|
IMGUI_IMPL_API bool ImGui_ImplSW_CreateDeviceObjects();
|
||||||
|
IMGUI_IMPL_API void ImGui_ImplSW_DestroyDeviceObjects();
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
|
@ -74,7 +74,8 @@ enum FurnaceGUIRenderBackend {
|
||||||
GUI_BACKEND_GL2,
|
GUI_BACKEND_GL2,
|
||||||
GUI_BACKEND_GL1,
|
GUI_BACKEND_GL1,
|
||||||
GUI_BACKEND_DX11,
|
GUI_BACKEND_DX11,
|
||||||
GUI_BACKEND_DX9
|
GUI_BACKEND_DX9,
|
||||||
|
GUI_BACKEND_SOFTWARE
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef HAVE_RENDER_DX11
|
#ifdef HAVE_RENDER_DX11
|
||||||
|
@ -99,7 +100,8 @@ enum FurnaceGUIRenderBackend {
|
||||||
#define GUI_BACKEND_DEFAULT GUI_BACKEND_SDL
|
#define GUI_BACKEND_DEFAULT GUI_BACKEND_SDL
|
||||||
#define GUI_BACKEND_DEFAULT_NAME "SDL"
|
#define GUI_BACKEND_DEFAULT_NAME "SDL"
|
||||||
#else
|
#else
|
||||||
#error how did you manage to do that?
|
#define GUI_BACKEND_DEFAULT GUI_BACKEND_SOFTWARE
|
||||||
|
#define GUI_BACKEDN_DEFAULT_NAME "Software"
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#ifdef HAVE_RENDER_DX11
|
#ifdef HAVE_RENDER_DX11
|
||||||
#include "render/renderDX11.h"
|
#include "render/renderDX11.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "render/renderSoftware.h"
|
||||||
|
|
||||||
bool FurnaceGUI::initRender() {
|
bool FurnaceGUI::initRender() {
|
||||||
if (rend!=NULL) return false;
|
if (rend!=NULL) return false;
|
||||||
|
@ -49,6 +50,8 @@ bool FurnaceGUI::initRender() {
|
||||||
renderBackend=GUI_BACKEND_DX9;
|
renderBackend=GUI_BACKEND_DX9;
|
||||||
} else if (settings.renderBackend=="SDL") {
|
} else if (settings.renderBackend=="SDL") {
|
||||||
renderBackend=GUI_BACKEND_SDL;
|
renderBackend=GUI_BACKEND_SDL;
|
||||||
|
} else if (settings.renderBackend=="Software") {
|
||||||
|
renderBackend=GUI_BACKEND_SOFTWARE;
|
||||||
} else {
|
} else {
|
||||||
renderBackend=GUI_BACKEND_DEFAULT;
|
renderBackend=GUI_BACKEND_DEFAULT;
|
||||||
}
|
}
|
||||||
|
@ -99,6 +102,10 @@ bool FurnaceGUI::initRender() {
|
||||||
rend=new FurnaceGUIRenderSDL;
|
rend=new FurnaceGUIRenderSDL;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case GUI_BACKEND_SOFTWARE:
|
||||||
|
logI("render backend: Software");
|
||||||
|
rend=new FurnaceGUIRenderSoftware;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
logE("invalid render backend!");
|
logE("invalid render backend!");
|
||||||
return false;
|
return false;
|
||||||
|
|
144
src/gui/render/renderSoftware.cpp
Normal file
144
src/gui/render/renderSoftware.cpp
Normal file
|
@ -0,0 +1,144 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "renderSoftware.h"
|
||||||
|
#include "imgui_sw.hpp"
|
||||||
|
#include "../../ta-log.h"
|
||||||
|
|
||||||
|
class FurnaceSoftwareTexture: public FurnaceGUITexture {
|
||||||
|
public:
|
||||||
|
SWTexture* tex;
|
||||||
|
FurnaceSoftwareTexture():
|
||||||
|
tex(NULL) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
ImTextureID FurnaceGUIRenderSoftware::getTextureID(FurnaceGUITexture* which) {
|
||||||
|
FurnaceSoftwareTexture* t=(FurnaceSoftwareTexture*)which;
|
||||||
|
return t->tex;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIRenderSoftware::lockTexture(FurnaceGUITexture* which, void** data, int* pitch) {
|
||||||
|
FurnaceSoftwareTexture* t=(FurnaceSoftwareTexture*)which;
|
||||||
|
if (!t->tex->managed) return false;
|
||||||
|
*data=t->tex->pixels;
|
||||||
|
*pitch=t->tex->width*(t->tex->isAlpha?1:4);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIRenderSoftware::unlockTexture(FurnaceGUITexture* which) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIRenderSoftware::updateTexture(FurnaceGUITexture* which, void* data, int pitch) {
|
||||||
|
FurnaceSoftwareTexture* t=(FurnaceSoftwareTexture*)which;
|
||||||
|
if (!t->tex->managed) return false;
|
||||||
|
memcpy(t->tex->pixels,data,pitch*t->tex->height);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FurnaceGUITexture* FurnaceGUIRenderSoftware::createTexture(bool dynamic, int width, int height, bool interpolate) {
|
||||||
|
FurnaceSoftwareTexture* ret=new FurnaceSoftwareTexture;
|
||||||
|
ret->tex=new SWTexture(width,height);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIRenderSoftware::destroyTexture(FurnaceGUITexture* which) {
|
||||||
|
FurnaceSoftwareTexture* t=(FurnaceSoftwareTexture*)which;
|
||||||
|
|
||||||
|
delete t->tex;
|
||||||
|
delete t;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::setBlendMode(FurnaceGUIBlendMode mode) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::clear(ImVec4 color) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIRenderSoftware::newFrame() {
|
||||||
|
return ImGui_ImplSW_NewFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIRenderSoftware::canVSync() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::createFontsTexture() {
|
||||||
|
ImGui_ImplSW_CreateFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::destroyFontsTexture() {
|
||||||
|
ImGui_ImplSW_DestroyFontsTexture();
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::renderGUI() {
|
||||||
|
ImGui_ImplSW_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::wipe(float alpha) {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::present() {
|
||||||
|
// TODO?
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIRenderSoftware::getOutputSize(int& w, int& h) {
|
||||||
|
SDL_Surface* surf=SDL_GetWindowSurface(sdlWin);
|
||||||
|
if (surf==NULL) return false;
|
||||||
|
w=surf->w;
|
||||||
|
h=surf->h;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FurnaceGUIRenderSoftware::getWindowFlags() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::setSwapInterval(int swapInterval) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::preInit() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIRenderSoftware::init(SDL_Window* win, int swapInterval) {
|
||||||
|
sdlWin=win;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::initGUI(SDL_Window* win) {
|
||||||
|
// hack
|
||||||
|
ImGui_ImplSDL2_InitForMetal(win);
|
||||||
|
ImGui_ImplSW_Init(win);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FurnaceGUIRenderSoftware::quitGUI() {
|
||||||
|
ImGui_ImplSW_Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FurnaceGUIRenderSoftware::quit() {
|
||||||
|
return true;
|
||||||
|
}
|
51
src/gui/render/renderSoftware.h
Normal file
51
src/gui/render/renderSoftware.h
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
/**
|
||||||
|
* Furnace Tracker - multi-system chiptune tracker
|
||||||
|
* Copyright (C) 2021-2024 tildearrow and contributors
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "../gui.h"
|
||||||
|
|
||||||
|
class FurnaceGUIRenderSoftware: public FurnaceGUIRender {
|
||||||
|
SDL_Window* sdlWin;
|
||||||
|
public:
|
||||||
|
ImTextureID getTextureID(FurnaceGUITexture* which);
|
||||||
|
bool lockTexture(FurnaceGUITexture* which, void** data, int* pitch);
|
||||||
|
bool unlockTexture(FurnaceGUITexture* which);
|
||||||
|
bool updateTexture(FurnaceGUITexture* which, void* data, int pitch);
|
||||||
|
FurnaceGUITexture* createTexture(bool dynamic, int width, int height, bool interpolate=true);
|
||||||
|
bool destroyTexture(FurnaceGUITexture* which);
|
||||||
|
void setTextureBlendMode(FurnaceGUITexture* which, FurnaceGUIBlendMode mode);
|
||||||
|
void setBlendMode(FurnaceGUIBlendMode mode);
|
||||||
|
void clear(ImVec4 color);
|
||||||
|
bool newFrame();
|
||||||
|
bool canVSync();
|
||||||
|
void createFontsTexture();
|
||||||
|
void destroyFontsTexture();
|
||||||
|
void renderGUI();
|
||||||
|
void wipe(float alpha);
|
||||||
|
void present();
|
||||||
|
bool getOutputSize(int& w, int& h);
|
||||||
|
int getWindowFlags();
|
||||||
|
void setSwapInterval(int swapInterval);
|
||||||
|
void preInit();
|
||||||
|
bool init(SDL_Window* win, int swapInterval);
|
||||||
|
void initGUI(SDL_Window* win);
|
||||||
|
void quitGUI();
|
||||||
|
bool quit();
|
||||||
|
FurnaceGUIRenderSoftware():
|
||||||
|
sdlWin(NULL) {}
|
||||||
|
};
|
|
@ -428,6 +428,10 @@ void FurnaceGUI::drawSettings() {
|
||||||
settingsChanged=true;
|
settingsChanged=true;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
if (ImGui::Selectable("Software",curRenderBackend=="Software")) {
|
||||||
|
settings.renderBackend="Software";
|
||||||
|
settingsChanged=true;
|
||||||
|
}
|
||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
if (ImGui::IsItemHovered()) {
|
if (ImGui::IsItemHovered()) {
|
||||||
|
|
Loading…
Reference in a new issue