From feb0bc995c2b2dc1fd55964dfd2e0878a77ec137 Mon Sep 17 00:00:00 2001 From: oxmox Date: Sun, 19 Feb 2023 01:24:52 +0100 Subject: [PATCH] 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! --- src/doompanning.cc | 161 +++++++++++++++++++++++++++++++++++++++++++-- src/dp_common.h | 6 +- 2 files changed, 162 insertions(+), 5 deletions(-) diff --git a/src/doompanning.cc b/src/doompanning.cc index 8cc4210..694a059 100644 --- a/src/doompanning.cc +++ b/src/doompanning.cc @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -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::value, @@ -42,7 +44,8 @@ struct ControllerContext std::vector dooms; bool quit = false; ExampleAppLog appLog; - int columns = 4; + int columns = 1; + std::array 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(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(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(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