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:
oxmox 2023-02-19 01:24:52 +01:00
parent 9dab0ccf70
commit feb0bc995c
2 changed files with 162 additions and 5 deletions

View file

@ -5,6 +5,7 @@
#include <SDL2/SDL.h>
#include <algorithm>
#include <array>
#include <cerrno>
#include <cstdlib>
#include <thread>
@ -26,8 +27,9 @@ void dp_sdl_fatal(const char *const msg)
struct DoomState
{
doomid_t id;
doomid_t id = 0;
DP_DoomState state = DP_DS_Unknown;
SDL_Texture *texture = nullptr;
};
static_assert(std::is_trivially_copyable<DoomState>::value,
@ -42,7 +44,8 @@ struct ControllerContext
std::vector<DoomState> dooms;
bool quit = false;
ExampleAppLog appLog;
int columns = 4;
int columns = 1;
std::array<u8, DoomScreenWidth * DoomScreenHeight * DoomBytesPerPixel> pixelBuffer;
};
struct ControllerActions
@ -53,7 +56,7 @@ struct ControllerActions
void spawn_doom(ControllerContext &ctx)
{
DoomState ds = {};
DoomState ds;
const char *const argv[] = { "doomsim", nullptr };
@ -66,6 +69,12 @@ void spawn_doom(ControllerContext &ctx)
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);
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)
{
#ifndef IMGUI_DISABLE_DEBUG_TOOLS
@ -308,7 +460,7 @@ int doom_controller_loop(ControllerContext &ctx)
SDL_SetRenderDrawColor(ctx.renderer, r, g, b, a);
SDL_RenderClear(ctx.renderer);
// TODO render dooms
render_dooms(ctx);
ImGui::Render();
ImGui_ImplSDLRenderer_RenderDrawData(ImGui::GetDrawData());
@ -370,6 +522,7 @@ int main(int argc, char *argv[])
ctx.sub = make_ctrl_sub(DoomUrl);
ctx.window = window;
ctx.renderer = renderer;
ctx.pixelBuffer.fill(0u);
log_add_callback(log_to_imgui, &ctx, LOG_TRACE);

View file

@ -16,6 +16,10 @@ extern "C" {
typedef pid_t doomid_t; // unique id for each doom instance
typedef u16 dmt_t; // for DP_MessageType values
#define DoomScreenWidth 320u
#define DoomScreenHeight 200u
#define DoomBytesPerPixel 4u
typedef enum DP_MessageType
{
DP_MT_Invalid,
@ -59,7 +63,7 @@ typedef struct __attribute__((packed, aligned(4)))
typedef struct __attribute__((packed, aligned(4)))
{
MessageBase head;
u8 frame[320 * 200];
u8 frame[DoomScreenWidth * DoomScreenHeight * DoomBytesPerPixel];
} MsgDoomFrame;
typedef struct __attribute__((packed, aligned(4)))