work on rendering - can draw rectangles into buffers and display those
Approach is to have one SDL_Texture per doom and use SDL_RenderCopy() with different destination rectangles to compose the layout. Apart from SDL_UpdateTexture() and SDL_RenderCopy() no work is done per doom. This scales up to ~200 doomsim instances. After that the frametime increases rapidly and all my cores are maxed out. TODO: test without spawning doomsim processes!
This commit is contained in:
parent
9dab0ccf70
commit
feb0bc995c
2 changed files with 162 additions and 5 deletions
|
@ -5,6 +5,7 @@
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
@ -26,8 +27,9 @@ void dp_sdl_fatal(const char *const msg)
|
||||||
|
|
||||||
struct DoomState
|
struct DoomState
|
||||||
{
|
{
|
||||||
doomid_t id;
|
doomid_t id = 0;
|
||||||
DP_DoomState state = DP_DS_Unknown;
|
DP_DoomState state = DP_DS_Unknown;
|
||||||
|
SDL_Texture *texture = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(std::is_trivially_copyable<DoomState>::value,
|
static_assert(std::is_trivially_copyable<DoomState>::value,
|
||||||
|
@ -42,7 +44,8 @@ struct ControllerContext
|
||||||
std::vector<DoomState> dooms;
|
std::vector<DoomState> dooms;
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
ExampleAppLog appLog;
|
ExampleAppLog appLog;
|
||||||
int columns = 4;
|
int columns = 1;
|
||||||
|
std::array<u8, DoomScreenWidth * DoomScreenHeight * DoomBytesPerPixel> pixelBuffer;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ControllerActions
|
struct ControllerActions
|
||||||
|
@ -53,7 +56,7 @@ struct ControllerActions
|
||||||
|
|
||||||
void spawn_doom(ControllerContext &ctx)
|
void spawn_doom(ControllerContext &ctx)
|
||||||
{
|
{
|
||||||
DoomState ds = {};
|
DoomState ds;
|
||||||
|
|
||||||
const char *const argv[] = { "doomsim", nullptr };
|
const char *const argv[] = { "doomsim", nullptr };
|
||||||
|
|
||||||
|
@ -66,6 +69,12 @@ void spawn_doom(ControllerContext &ctx)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ds.texture = SDL_CreateTexture(ctx.renderer, SDL_PIXELFORMAT_ARGB8888,
|
||||||
|
SDL_TEXTUREACCESS_STREAMING, DoomScreenWidth, DoomScreenHeight);
|
||||||
|
|
||||||
|
if (!ds.texture)
|
||||||
|
dp_sdl_fatal("SDL_CreateTexture");
|
||||||
|
|
||||||
ctx.dooms.emplace_back(ds);
|
ctx.dooms.emplace_back(ds);
|
||||||
|
|
||||||
log_info("Spawned new doom, pid=%d", ds.id);
|
log_info("Spawned new doom, pid=%d", ds.id);
|
||||||
|
@ -172,6 +181,149 @@ void final_cleanup(ControllerContext &ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline s32 RoundFloatToInt(float value)
|
||||||
|
{
|
||||||
|
return static_cast<s32>(value + 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct V4: public ImVec4
|
||||||
|
{
|
||||||
|
using ImVec4::ImVec4;
|
||||||
|
float &a = ImVec4::w;
|
||||||
|
float &r = ImVec4::x;
|
||||||
|
float &g = ImVec4::y;
|
||||||
|
float &b = ImVec4::z;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline u32 V4ToARGB(V4 c)
|
||||||
|
{
|
||||||
|
u32 result = ((RoundFloatToInt(c.a * 255.0) & 0xff) << 24
|
||||||
|
| (RoundFloatToInt(c.r * 255.0) & 0xff) << 16
|
||||||
|
| (RoundFloatToInt(c.g * 255.0) & 0xff) << 8
|
||||||
|
| (RoundFloatToInt(c.b * 255.0) & 0xff) << 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OffscreenBuffer
|
||||||
|
{
|
||||||
|
s32 width;
|
||||||
|
s32 height;
|
||||||
|
u8 *pixels;
|
||||||
|
s32 pitch;
|
||||||
|
const int BytesPerPixel;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline void PutPixelUnchecked(OffscreenBuffer *buffer, s32 x, s32 y, V4 color)
|
||||||
|
{
|
||||||
|
u32 *pixel = reinterpret_cast<u32 *>(buffer->pixels
|
||||||
|
+ y * buffer->pitch
|
||||||
|
+ x * buffer->BytesPerPixel);
|
||||||
|
*pixel = V4ToARGB(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void PutPixel(OffscreenBuffer *buffer, s32 x, s32 y, V4 color)
|
||||||
|
{
|
||||||
|
if (x >= 0 && x < buffer->width && y >= 0 && y < buffer->height)
|
||||||
|
{
|
||||||
|
PutPixelUnchecked(buffer, x, y, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DrawRectangle(OffscreenBuffer *buffer,
|
||||||
|
float realMinX, float realMinY,
|
||||||
|
float realMaxX, float realMaxY,
|
||||||
|
V4 color)
|
||||||
|
|
||||||
|
{
|
||||||
|
s32 minX = RoundFloatToInt(realMinX);
|
||||||
|
s32 minY = RoundFloatToInt(realMinY);
|
||||||
|
s32 maxX = RoundFloatToInt(realMaxX);
|
||||||
|
s32 maxY = RoundFloatToInt(realMaxY);
|
||||||
|
|
||||||
|
if (minX < 0)
|
||||||
|
minX = 0;
|
||||||
|
|
||||||
|
if (minY < 0)
|
||||||
|
minY = 0;
|
||||||
|
|
||||||
|
if (maxX > buffer->width)
|
||||||
|
maxX = buffer->width;
|
||||||
|
|
||||||
|
if (maxY > buffer->height)
|
||||||
|
maxY = buffer->height;
|
||||||
|
|
||||||
|
|
||||||
|
u8 *row = buffer->pixels + minY * buffer->pitch;
|
||||||
|
|
||||||
|
for (s32 y = minY; y < maxY; ++y)
|
||||||
|
{
|
||||||
|
u32 *dstPixel = reinterpret_cast<u32 *>(row + minX * buffer->BytesPerPixel);
|
||||||
|
|
||||||
|
for (s32 x = minX; x < maxX; ++x)
|
||||||
|
{
|
||||||
|
float a = color.a;
|
||||||
|
float srcR = color.r * 255.0;
|
||||||
|
float srcG = color.g * 255.0;
|
||||||
|
float srcB = color.b * 255.0;
|
||||||
|
|
||||||
|
float dstR = ((*dstPixel >> 16) & 0xff);
|
||||||
|
float dstG = ((*dstPixel >> 8) & 0xff);
|
||||||
|
float dstB = ((*dstPixel >> 0) & 0xff);
|
||||||
|
|
||||||
|
dstR = (1 - a) * dstR + a * srcR;
|
||||||
|
dstG = (1 - a) * dstG + a * srcG;
|
||||||
|
dstB = (1 - a) * dstB + a * srcB;
|
||||||
|
|
||||||
|
*dstPixel = (RoundFloatToInt(dstR) << 16 |
|
||||||
|
RoundFloatToInt(dstG) << 8 |
|
||||||
|
RoundFloatToInt(dstB) << 0);
|
||||||
|
|
||||||
|
++dstPixel;
|
||||||
|
}
|
||||||
|
|
||||||
|
row += buffer->pitch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void render_dooms(ControllerContext &ctx)
|
||||||
|
{
|
||||||
|
OffscreenBuffer buffer =
|
||||||
|
{
|
||||||
|
DoomScreenWidth,
|
||||||
|
DoomScreenHeight,
|
||||||
|
ctx.pixelBuffer.data(),
|
||||||
|
DoomScreenWidth * DoomBytesPerPixel,
|
||||||
|
DoomBytesPerPixel
|
||||||
|
};
|
||||||
|
|
||||||
|
DrawRectangle(&buffer, 0, 0, 10, 10, { 1, 0, 0, 1 }); // top-left red
|
||||||
|
DrawRectangle(&buffer, 0, buffer.height-10, 10, buffer.height, { 0, 1, 0, 1 });
|
||||||
|
DrawRectangle(&buffer, buffer.width-10, buffer.height-10, buffer.width, buffer.height, { 0, 0, 1, 1 });
|
||||||
|
DrawRectangle(&buffer, buffer.width-10, 0, buffer.width, 10, { 0.840, 0.0168, 0.717, 1 });
|
||||||
|
|
||||||
|
//DrawRectangle(&buffer, buffer.width-10, 0, buffer.width, 10, { 0.0, 1.0, 0.0, 1.0 }); // top-right green
|
||||||
|
|
||||||
|
SDL_Rect destRect = {0, 0, buffer.width, buffer.height};
|
||||||
|
const size_t doomCount = ctx.dooms.size();
|
||||||
|
|
||||||
|
for (size_t i=0; i<doomCount; ++i)
|
||||||
|
{
|
||||||
|
if (i != 0 && i % ctx.columns == 0)
|
||||||
|
{
|
||||||
|
destRect.x = 0;
|
||||||
|
destRect.y += buffer.height;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto &ds = ctx.dooms.at(i);
|
||||||
|
auto texture = ds.texture;
|
||||||
|
assert(texture);
|
||||||
|
SDL_UpdateTexture(texture, nullptr, buffer.pixels, buffer.width * buffer.BytesPerPixel);
|
||||||
|
SDL_RenderCopy(ctx.renderer, texture, nullptr, &destRect);
|
||||||
|
|
||||||
|
destRect.x += buffer.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ControllerActions run_ui(ControllerContext &ctx)
|
ControllerActions run_ui(ControllerContext &ctx)
|
||||||
{
|
{
|
||||||
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
|
||||||
|
@ -308,7 +460,7 @@ int doom_controller_loop(ControllerContext &ctx)
|
||||||
SDL_SetRenderDrawColor(ctx.renderer, r, g, b, a);
|
SDL_SetRenderDrawColor(ctx.renderer, r, g, b, a);
|
||||||
SDL_RenderClear(ctx.renderer);
|
SDL_RenderClear(ctx.renderer);
|
||||||
|
|
||||||
// TODO render dooms
|
render_dooms(ctx);
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData());
|
ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
@ -370,6 +522,7 @@ int main(int argc, char *argv[])
|
||||||
ctx.sub = make_ctrl_sub(DoomUrl);
|
ctx.sub = make_ctrl_sub(DoomUrl);
|
||||||
ctx.window = window;
|
ctx.window = window;
|
||||||
ctx.renderer = renderer;
|
ctx.renderer = renderer;
|
||||||
|
ctx.pixelBuffer.fill(0u);
|
||||||
|
|
||||||
log_add_callback(log_to_imgui, &ctx, LOG_TRACE);
|
log_add_callback(log_to_imgui, &ctx, LOG_TRACE);
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,10 @@ extern "C" {
|
||||||
typedef pid_t doomid_t; // unique id for each doom instance
|
typedef pid_t doomid_t; // unique id for each doom instance
|
||||||
typedef u16 dmt_t; // for DP_MessageType values
|
typedef u16 dmt_t; // for DP_MessageType values
|
||||||
|
|
||||||
|
#define DoomScreenWidth 320u
|
||||||
|
#define DoomScreenHeight 200u
|
||||||
|
#define DoomBytesPerPixel 4u
|
||||||
|
|
||||||
typedef enum DP_MessageType
|
typedef enum DP_MessageType
|
||||||
{
|
{
|
||||||
DP_MT_Invalid,
|
DP_MT_Invalid,
|
||||||
|
@ -59,7 +63,7 @@ typedef struct __attribute__((packed, aligned(4)))
|
||||||
typedef struct __attribute__((packed, aligned(4)))
|
typedef struct __attribute__((packed, aligned(4)))
|
||||||
{
|
{
|
||||||
MessageBase head;
|
MessageBase head;
|
||||||
u8 frame[320 * 200];
|
u8 frame[DoomScreenWidth * DoomScreenHeight * DoomBytesPerPixel];
|
||||||
} MsgDoomFrame;
|
} MsgDoomFrame;
|
||||||
|
|
||||||
typedef struct __attribute__((packed, aligned(4)))
|
typedef struct __attribute__((packed, aligned(4)))
|
||||||
|
|
Loading…
Reference in a new issue