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 <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);
|
||||
|
||||
|
|
|
@ -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)))
|
||||
|
|
Loading…
Reference in a new issue