Skip to content

Commit

Permalink
sokol: Tweak buffer pooling
Browse files Browse the repository at this point in the history
  • Loading branch information
IonAgorria committed May 10, 2024
1 parent b9b5ce9 commit cffd181
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 105 deletions.
20 changes: 14 additions & 6 deletions Source/Render/sokol/SokolRender.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,14 +419,23 @@ void cSokolRender::DeleteIndexBuffer(IndexBuffer &ib) {

void cSokolRender::ClearCommands() {
for (SokolCommand* command : commands) {
//Reclaim resources that can be reused
SokolResourceBuffer* vertex_buffer = command->vertex_buffer;
if (vertex_buffer && vertex_buffer->key != SokolResourceKeyNone) {
command->vertex_buffer = nullptr;
xassert(vertex_buffer->RefCount() <= 50);
bufferPool.emplace(vertex_buffer->key, vertex_buffer);
}
SokolResourceBuffer* index_buffer = command->index_buffer;
if (index_buffer && index_buffer->key != SokolResourceKeyNone) {
command->index_buffer = nullptr;
xassert(index_buffer->RefCount() <= 50);
bufferPool.emplace(index_buffer->key, index_buffer);
}

delete command;
}
commands.clear();
for (auto & it : bufferCache) {
for (auto bufferIt = it.second.begin(); bufferIt != it.second.end(); bufferIt++) {
bufferIt->used = false;
}
}
}

void cSokolRender::ClearPipelines() {
Expand Down Expand Up @@ -594,7 +603,6 @@ void SokolCommand::ClearDrawData() {
}
vertices = 0;
indices = 0;

}

void SokolCommand::ClearShaderParams() {
Expand Down
29 changes: 6 additions & 23 deletions Source/Render/sokol/SokolRender.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,29 +12,6 @@

const int PERIMETER_SOKOL_TEXTURES = 8;

struct SokolBufferCacheKey {
size_t len;
sg_buffer_type type;

bool operator==(const SokolBufferCacheKey &other) const {
return len == other.len && type == other.type;
}
};

template<>
struct std::hash<SokolBufferCacheKey> {
std::size_t operator()(const SokolBufferCacheKey& k) const {
return k.len * k.type;
}
};

struct SokolBufferCacheValue {
bool used;
SokolResourceBuffer* buffer;
};

extern std::unordered_map<SokolBufferCacheKey, std::list<SokolBufferCacheValue>> bufferCache;

struct SokolCommand {
SokolCommand();
~SokolCommand();
Expand Down Expand Up @@ -82,6 +59,9 @@ class cSokolRender: public cInterfaceRenderDevice {
friend const void* sokol_d3d_depth_stencil_view_cb();
#endif

//Stores resources for reusing
std::unordered_multimap<uint64_t, SokolResourceBuffer*> bufferPool;

//For swapchain pass that renders into final device
sg_pass swapchain_pass;

Expand Down Expand Up @@ -136,6 +116,9 @@ class cSokolRender: public cInterfaceRenderDevice {
void SetMaterial(SOKOL_MATERIAL_TYPE material, const sColor4f& diffuse, const sColor4f& ambient,
const sColor4f& specular, const sColor4f& emissive, float power);

///Assigns unused sokol buffer to buffer_ptr with requested
void PrepareSokolBuffer(SokolBuffer*& buffer_ptr, MemoryResource* resource, size_t len, bool dynamic, sg_buffer_type type);

//Updates internal state after init/resolution change
int UpdateRenderMode();

Expand Down
116 changes: 77 additions & 39 deletions Source/Render/sokol/SokolRenderState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ void sokol_metal_render_callback() {
}
#endif

std::unordered_map<SokolBufferCacheKey, std::list<SokolBufferCacheValue>> bufferCache;

int cSokolRender::BeginScene() {
RenderSubmitEvent(RenderEvent::BEGIN_SCENE, ActiveScene ? "ActiveScene" : "");
MTG();
Expand All @@ -62,6 +60,7 @@ int cSokolRender::EndScene() {
//Make sure there is nothing left to send as command
FinishActiveDrawBuffer();

xassert(activeDrawBuffer == nullptr);
ActiveScene = false;

#ifdef SOKOL_METAL
Expand Down Expand Up @@ -291,69 +290,65 @@ int cSokolRender::Fill(int r, int g, int b, int a) {
return 0;
}

void CreateSokolBuffer(SokolBuffer*& buffer_ptr, MemoryResource* resource, size_t len, bool dynamic, sg_buffer_type type) {
void cSokolRender::PrepareSokolBuffer(SokolBuffer*& buffer_ptr, MemoryResource* resource, size_t len, bool dynamic, sg_buffer_type type) {
MT_IS_GRAPH();
xassert(!resource->locked);
xassert(len <= resource->data_len);

SokolResourceBuffer* buffer = nullptr;
SokolResourceBuffer* buffer;
if (dynamic) {
std::list<SokolBufferCacheValue> *candidates = nullptr;
SokolBufferCacheKey key {len, type };
auto it = bufferCache.find(key);
if (it == bufferCache.end()) {
candidates = &bufferCache[key];
} else {
candidates = &it->second;
}

for (auto candidate = candidates->begin(); candidate != candidates->end(); ++candidate) {
if (!candidate->used) {
candidate->used = true;
buffer = candidate->buffer;
break;
}
}

if (buffer == nullptr) {
//Get power of 2 to hold len
size_t i = 1;
while (i < len) {
i = i << 1;
}
len = i;
SokolResourceKey resource_key = get_sokol_resource_key_buffer(len, type);
auto nh = bufferPool.extract(resource_key);
if (nh.empty()) {
sg_buffer_desc desc = {};
desc.size = len;
desc.type = type;
desc.usage = SG_USAGE_STREAM;
if (type == SG_BUFFERTYPE_VERTEXBUFFER) {
desc.label = "CreateVertexBuffer";
desc.label = "VertexBufferStream";
} else if (type == SG_BUFFERTYPE_INDEXBUFFER) {
desc.label = "CreateIndexBuffer";
} else {
desc.label = "CreateSokolBuffer";
desc.label = "IndexBufferStream";
}

resource->dirty = true;

sg_buffer sg_buffer = sg_make_buffer(&desc);
buffer = new SokolResourceBuffer(sg_buffer, false);
buffer->IncRef();
candidates->push_back(SokolBufferCacheValue {
true,
buffer,
});
buffer = new SokolResourceBuffer(
resource_key,
sg_buffer
);
} else {
buffer = nh.mapped();
}

resource->dirty = true;
} else {
sg_buffer_desc desc = {};
desc.size = len;
desc.type = type;
desc.usage = SG_USAGE_IMMUTABLE;
if (type == SG_BUFFERTYPE_VERTEXBUFFER) {
desc.label = "VertexBufferImmutable";
} else if (type == SG_BUFFERTYPE_INDEXBUFFER) {
desc.label = "IndexBufferImmutable";
}
xassert(buffer_ptr == nullptr);
xassert(resource->data);
xassert(!resource->burned);
desc.data = {resource->data, len};
resource->burned = true;
resource->dirty = false;

buffer = new SokolResourceBuffer(sg_make_buffer(&desc));
buffer = new SokolResourceBuffer(
SokolResourceKeyNone,
sg_make_buffer(&desc)
);
}


xassert(buffer != nullptr);

if (buffer_ptr == nullptr) {
buffer_ptr = new SokolBuffer(buffer);
Expand Down Expand Up @@ -391,6 +386,7 @@ void cSokolRender::FinishActiveDrawBuffer() {
}

void cSokolRender::CreateCommand(VertexBuffer* vb, size_t vertices, IndexBuffer* ib, size_t indices) {
xassert(ActiveScene);
MT_IS_GRAPH();
if (0 == vertices) vertices = activeCommand.vertices;
if (0 == indices) indices = activeCommand.indices;
Expand Down Expand Up @@ -431,7 +427,7 @@ void cSokolRender::CreateCommand(VertexBuffer* vb, size_t vertices, IndexBuffer*
if (vb->data && (vb->sg == nullptr || vb->dirty)) {
size_t len = vertices * vb->VertexSize;
if (vb->sg == nullptr || vb->sg->buffer == nullptr) {
CreateSokolBuffer(vb->sg, vb, len, vb->dynamic, SG_BUFFERTYPE_VERTEXBUFFER);
PrepareSokolBuffer(vb->sg, vb, len, vb->dynamic, SG_BUFFERTYPE_VERTEXBUFFER);
}
if (vb->dynamic) {
vb->sg->update(vb, len);
Expand All @@ -440,7 +436,7 @@ void cSokolRender::CreateCommand(VertexBuffer* vb, size_t vertices, IndexBuffer*
if (ib->data && (!ib->sg || ib->dirty)) {
size_t len = indices * sizeof(indices_t);
if (ib->sg == nullptr || ib->sg->buffer == nullptr) {
CreateSokolBuffer(ib->sg, ib, len, ib->dynamic, SG_BUFFERTYPE_INDEXBUFFER);
PrepareSokolBuffer(ib->sg, ib, len, ib->dynamic, SG_BUFFERTYPE_INDEXBUFFER);
}
if (ib->dynamic) {
ib->sg->update(ib, len);
Expand Down Expand Up @@ -701,6 +697,48 @@ void cSokolRender::SetTextureImage(uint32_t slot, TextureImage* texture_image) {
return;
}
if (tex->dirty) {
//Create image resource
if (!tex->image) {
sg_image_desc*& desc = tex->desc;
xassert(desc);
#ifdef PERIMETER_DEBUG
if (!tex->label.empty()) {
desc->label = tex->label.c_str();
}
#endif
xassert(desc->usage == SG_USAGE_IMMUTABLE || tex->data);
SokolResourceKey resource_key = SokolResourceKeyNone;
if (desc->usage == SG_USAGE_STREAM) {
resource_key = get_sokol_resource_key_texture(
desc->width,
desc->height, desc->pixel_format);
}
tex->image = new SokolResourceTexture(
resource_key,
sg_make_image(desc)
);
tex->resource_key = tex->image->key;
if (desc->usage == SG_USAGE_IMMUTABLE) {
//Cleanup subimages
for (int ci = 0; ci < SG_CUBEFACE_NUM; ++ci) {
for (int i = 0; i < SG_MAX_MIPMAPS; ++i) {
sg_range& range = desc->data.subimage[ci][i];
if (!range.ptr) {
break;
}
const uint8_t* buf = reinterpret_cast<const uint8_t*>(range.ptr);
delete[] buf;
}
}
tex->FreeData();
}

//We no longer need desc for this
delete desc;
desc = nullptr;
}

//Update the texture
tex->update();
}
if (activeCommand.sokol_textures[slot] != tex->image) {
Expand Down
2 changes: 1 addition & 1 deletion Source/Render/sokol/SokolRenderTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ int cSokolRender::CreateTexture(cTexture* Texture, cFileImage* FileImage, bool e
desc->num_mipmaps = std::min(static_cast<int>(SG_MAX_MIPMAPS), Texture->GetNumberMipMap());

if (!FileImage) {
desc->usage = SG_USAGE_DYNAMIC;
desc->usage = SG_USAGE_STREAM;
img = new SokolTexture2D(desc);
} else {
desc->usage = SG_USAGE_IMMUTABLE;
Expand Down
48 changes: 18 additions & 30 deletions Source/Render/sokol/SokolResources.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@
#include "SokolResources.h"
#include "SokolTypes.h"

SokolResourceKey get_sokol_resource_key_buffer(size_t len, sg_buffer_type type) {
return (len << 16) | static_cast<uint16_t>(type);
}

SokolResourceKey get_sokol_resource_key_texture(uint32_t w, uint32_t h, sg_pixel_format format) {
size_t len = w;
len << 32;

Check warning on line 13 in Source/Render/sokol/SokolResources.cpp

View workflow job for this annotation

GitHub Actions / Debug x86 64 bits

'<<': result of expression not used

Check warning on line 13 in Source/Render/sokol/SokolResources.cpp

View workflow job for this annotation

GitHub Actions / Release x86 32 bits

'<<': shift count negative or too big, undefined behavior

Check warning on line 13 in Source/Render/sokol/SokolResources.cpp

View workflow job for this annotation

GitHub Actions / Release x86 32 bits

'<<': result of expression not used

Check warning on line 13 in Source/Render/sokol/SokolResources.cpp

View workflow job for this annotation

GitHub Actions / Release x86 64 bits

'<<': result of expression not used
len |= h;
len << 8;

Check warning on line 15 in Source/Render/sokol/SokolResources.cpp

View workflow job for this annotation

GitHub Actions / Debug x86 64 bits

'<<': result of expression not used

Check warning on line 15 in Source/Render/sokol/SokolResources.cpp

View workflow job for this annotation

GitHub Actions / Release x86 32 bits

'<<': result of expression not used

Check warning on line 15 in Source/Render/sokol/SokolResources.cpp

View workflow job for this annotation

GitHub Actions / Release x86 64 bits

'<<': result of expression not used
len |= static_cast<uint8_t>(sokol_pixelformat_bytesize(format));
return len;
}

size_t sokol_pixelformat_bytesize(sg_pixel_format fmt) {
//Probably the only pixel format used, so we cache it
if (fmt == SG_PIXELFORMAT_RGBA8) {
Expand All @@ -29,11 +42,8 @@ void SokolResourceTexture::destroy_res() {
}
}

SokolBuffer::SokolBuffer(sg_buffer _buffer) {
buffer = new SokolResource(_buffer);
}

SokolBuffer::SokolBuffer(SokolResource<sg_buffer> *buffer): buffer(buffer) {
xassert(buffer);
}

SokolBuffer::~SokolBuffer() {
Expand Down Expand Up @@ -89,32 +99,10 @@ void SokolTexture2D::update() {
if (!dirty) return;
dirty = false;

if (desc) {
#ifdef PERIMETER_DEBUG
if (!label.empty()) {
desc->label = label.c_str();
}
#endif
xassert(desc->usage == SG_USAGE_IMMUTABLE || data);
image = new SokolResource(sg_make_image(desc));
if (desc->usage == SG_USAGE_IMMUTABLE) {
//Cleanup subimages
for (int ci = 0; ci < SG_CUBEFACE_NUM; ++ci) {
for (int i = 0; i < SG_MAX_MIPMAPS; ++i) {
sg_range& range = desc->data.subimage[ci][i];
if (!range.ptr) {
break;
}
const uint8_t* buf = reinterpret_cast<const uint8_t*>(range.ptr);
delete[] buf;
}
}
FreeData();
}
delete desc;
desc = nullptr;
}

if (!image) {
xassert(0);
return;
}
if (data) {
xassert(image->res.id != SG_INVALID_ID);
sg_image_data imageData;
Expand Down

0 comments on commit cffd181

Please sign in to comment.