diff --git a/Source/Render/inc/StdAfxRD.h b/Source/Render/inc/StdAfxRD.h index 429da212..f31524e9 100644 --- a/Source/Render/inc/StdAfxRD.h +++ b/Source/Render/inc/StdAfxRD.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #if (!defined(_FINAL_VERSION_) || defined(_DEBUG)) && !defined(NASSERT) diff --git a/Source/Render/sokol/SokolRender.cpp b/Source/Render/sokol/SokolRender.cpp index 32421ce0..0e015e54 100644 --- a/Source/Render/sokol/SokolRender.cpp +++ b/Source/Render/sokol/SokolRender.cpp @@ -64,7 +64,7 @@ int cSokolRender::Init(int xScr, int yScr, int mode, SDL_Window* wnd, int Refres } ClearPooledResources(0); - ClearCommands(); + ClearAllCommands(); ClearPipelines(); const char* sokol_backend = "Unknown"; @@ -302,6 +302,7 @@ int cSokolRender::Init(int xScr, int yScr, int mode, SDL_Window* wnd, int Refres #ifdef PERIMETER_DEBUG emptyTexture->label = "EmptySlotTexture"; #endif + PrepareSokolTexture(emptyTexture); for (int i = 0; i < PERIMETER_SOKOL_TEXTURES; ++i) { activeTextureTransform[i] = Mat4f::ID; @@ -364,7 +365,7 @@ int cSokolRender::Done() { int ret = cInterfaceRenderDevice::Done(); activeCommand.Clear(); ClearPooledResources(0); - ClearCommands(); + ClearAllCommands(); ClearPipelines(); shaders.clear(); delete emptyTexture; @@ -429,74 +430,101 @@ void cSokolRender::DeleteIndexBuffer(IndexBuffer &ib) { #define ClearPooledResources_Debug 0 void cSokolRender::ClearPooledResources(uint32_t max_life) { - if (bufferPool.empty()) { + if (bufferPool.empty() && imagePool.empty()) { return; } #if defined(PERIMETER_DEBUG) && ClearPooledResources_Debug - size_t count = bufferPool.size(); + size_t bufferCount = bufferPool.size(); + size_t imageCount = imagePool.size(); #endif - auto it = bufferPool.begin(); - while (it != bufferPool.end()) { - auto& res = it->second; - res.unused_since++; - if (res.unused_since >= max_life) { - res.resource->DecRef(); - res.resource = nullptr; - it = bufferPool.erase(it); - } else { - it++; + { + auto it = bufferPool.begin(); + while (it != bufferPool.end()) { + auto& res = it->second; + res.unused_since++; + if (res.unused_since >= max_life) { + res.resource->DecRef(); + res.resource = nullptr; + it = bufferPool.erase(it); + } else { + it++; + } + } + } + { + auto it = imagePool.begin(); + while (it != imagePool.end()) { + auto& res = it->second; + res.unused_since++; + if (res.unused_since >= max_life) { + res.resource->DecRef(); + res.resource = nullptr; + it = imagePool.erase(it); + } else { + it++; + } } } #if defined(PERIMETER_DEBUG) && ClearPooledResources_Debug - if (count != bufferPool.size()) { - printf("ClearPooledResources %" PRIsize " -> %" PRIsize "\n", count, bufferPool.size()); + if (bufferCount != bufferPool.size()) { + printf("ClearPooledResources buffers %" PRIsize " -> %" PRIsize "\n", bufferCount, bufferPool.size()); + } + if (imageCount != imagePool.size()) { + printf("ClearPooledResources images %" PRIsize " -> %" PRIsize "\n", imageCount, imagePool.size()); } #endif } -void cSokolRender::ClearCommands() { - std::unordered_set pooled; - auto ClearCommands = [&pooled, this](std::vector& commands) { - for (SokolCommand* command : commands) { - //Reclaim resources that can be reused - SokolResourceBuffer* vertex_buffer = command->vertex_buffer; - if (vertex_buffer && vertex_buffer->key != SokolResourceKeyNone && pooled.count(vertex_buffer) == 0) { - command->vertex_buffer = nullptr; - xassert(0 < vertex_buffer->RefCount() && vertex_buffer->RefCount() <= 50); - bufferPool.emplace( - vertex_buffer->key, - SokolResourcePooled(vertex_buffer) - ); - pooled.emplace(vertex_buffer); - } - SokolResourceBuffer* index_buffer = command->index_buffer; - if (index_buffer && index_buffer->key != SokolResourceKeyNone && pooled.count(index_buffer) == 0) { - command->index_buffer = nullptr; - xassert(0 < index_buffer->RefCount() && index_buffer->RefCount() <= 50); - bufferPool.emplace( - index_buffer->key, - SokolResourcePooled(index_buffer) - ); - pooled.emplace(index_buffer); +#define CanStorePooledResource(res) (res && !res->pooled && res->key != SokolResourceKeyNone) + +template +void StorePooledResource( + std::unordered_multimap>& res_pool, + SokolResource*& res +) { + xassert(CanStorePooledResource(res)); + xassert(0 < res->RefCount() && res->RefCount() <= 10000); + res->IncRef(); + res->pooled = true; + res->burned = false; + res_pool.emplace( + res->key, + SokolResourcePooled(res) + ); +} + +void cSokolRender::ClearCommands(std::vector& commands_to_clear) { + for (SokolCommand* command : commands_to_clear) { + //Reclaim resources that can be reused + if (CanStorePooledResource(command->vertex_buffer)) { + StorePooledResource(bufferPool, command->vertex_buffer); + } + if (CanStorePooledResource(command->index_buffer)) { + StorePooledResource(bufferPool, command->index_buffer); + } + for (SokolResourceImage* image : command->sokol_images) { + if (CanStorePooledResource(image)) { + StorePooledResource(imagePool, image); } - - delete command; } - }; - for (auto& target : std::array{shadowMapRenderTarget, lightMapRenderTarget}) { + delete command; + } + + commands_to_clear.clear(); +} + +void cSokolRender::ClearAllCommands() { + for (auto& target : { + shadowMapRenderTarget, + lightMapRenderTarget + }) { if (target != nullptr) { ClearCommands(target->commands); - target->commands.clear(); } } ClearCommands(commands); - commands.clear(); - -#ifdef PERIMETER_DEBUG - //printf("%ld %ld\n", reclaimed.size(), bufferPool.size()); -#endif } void cSokolRender::ClearPipelines() { @@ -712,25 +740,25 @@ void SokolCommand::ClearShaderParams() { fs_params_len = 0; } -void SokolCommand::ClearTextures() { +void SokolCommand::ClearImages() { for (int i = 0; i < PERIMETER_SOKOL_TEXTURES; ++i) { - SetTexture(i, nullptr); + SetImage(i, nullptr); } } void SokolCommand::Clear() { ClearDrawData(); ClearShaderParams(); - ClearTextures(); + ClearImages(); } -void SokolCommand::SetTexture(size_t index, SokolResourceTexture* texture) { +void SokolCommand::SetImage(size_t index, SokolResourceImage* image) { xassert(indexIncRef(); + if (image) { + image->IncRef(); } - if (sokol_textures[index]) { - sokol_textures[index]->DecRef(); + if (sokol_images[index]) { + sokol_images[index]->DecRef(); } - sokol_textures[index] = texture; + sokol_images[index] = image; } diff --git a/Source/Render/sokol/SokolRender.h b/Source/Render/sokol/SokolRender.h index 855c095d..9da7d483 100644 --- a/Source/Render/sokol/SokolRender.h +++ b/Source/Render/sokol/SokolRender.h @@ -5,14 +5,12 @@ #define PERIMETER_SOKOL_GL (1) #endif -#include - #include #include #include "SokolTypes.h" -const int PERIMETER_SOKOL_TEXTURES = 8; +const int PERIMETER_SOKOL_TEXTURES = 4; struct SokolCommand { SokolCommand(); @@ -21,8 +19,8 @@ struct SokolCommand { void Clear(); void ClearDrawData(); void ClearShaderParams(); - void SetTexture(size_t index, SokolResource* sokol_texture); - void ClearTextures(); + void SetImage(size_t index, SokolResourceImage* image); + void ClearImages(); NO_COPY_CONSTRUCTOR(SokolCommand); struct SokolPipeline* pipeline = nullptr; @@ -30,7 +28,7 @@ struct SokolCommand { size_t base_elements = 0; size_t vertices = 0; size_t indices = 0; - SokolResourceTexture* sokol_textures[PERIMETER_SOKOL_TEXTURES] = {}; + SokolResourceImage* sokol_images[PERIMETER_SOKOL_TEXTURES] = {}; SokolResourceBuffer* vertex_buffer = nullptr; SokolResourceBuffer* index_buffer = nullptr; void* vs_params = nullptr; @@ -98,6 +96,7 @@ class cSokolRender: public cInterfaceRenderDevice { //Stores resources for reusing void ClearPooledResources(uint32_t max_life); std::unordered_multimap> bufferPool; + std::unordered_multimap> imagePool; //For swapchain pass that renders into final device sg_swapchain swapchain = {}; @@ -133,8 +132,9 @@ class cSokolRender: public cInterfaceRenderDevice { //Active pipeline/command state SokolCommand activeCommand; + SokolTexture2D* activeCommandTextures[PERIMETER_SOKOL_TEXTURES] = {}; PIPELINE_TYPE activePipelineType = PIPELINE_TYPE_DEFAULT; - PIPELINE_MODE activePipelineMode; + SokolPipelineMode activePipelineMode; Mat4f activeCommandVP; Mat4f activeCommandW; eColorMode activeCommandColorMode = COLOR_MOD; @@ -161,7 +161,8 @@ class cSokolRender: public cInterfaceRenderDevice { //Commands handling void ClearActiveBufferAndPassAction(); - void ClearCommands(); + void ClearCommands(std::vector& commands); + void ClearAllCommands(); void FinishActiveDrawBuffer(); void CreateCommandEmpty(); void CreateCommand(class VertexBuffer* vb, size_t vertices, class IndexBuffer* ib, size_t indices); @@ -173,13 +174,16 @@ class cSokolRender: public cInterfaceRenderDevice { ///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); + + ///Prepares internal sokol image + void PrepareSokolTexture(SokolTexture2D* tex); //Updates internal state after init/resolution change int UpdateRenderMode(); //Does actual drawing using sokol API void DoSokolRendering(); - void DoSokolRendering(sg_pass& render_pass, const std::vector& commands); + void ProcessRenderPass(sg_pass& render_pass, const std::vector& commands); //Set common VS/FS parameters template diff --git a/Source/Render/sokol/SokolRenderDraw.cpp b/Source/Render/sokol/SokolRenderDraw.cpp index dc18afa7..27ab9038 100644 --- a/Source/Render/sokol/SokolRenderDraw.cpp +++ b/Source/Render/sokol/SokolRenderDraw.cpp @@ -241,8 +241,8 @@ void cSokolRender::SetMaterialTilemap(cTileMap *TileMap) { Mat4f matlight = pShadow->matViewProj; activeShadowMatrix = matlight * matTexAdj; - activeCommand.SetTexture(0, pShadowMap->GetFrameImage(0)->sg->image); - activeCommand.SetTexture(2, pLightMap->GetFrameImage(0)->sg->image); + SetTextureImage(0, pShadowMap->GetFrameImage(0)); + SetTextureImage(2, pLightMap->GetFrameImage(0)); TerraInterface* terra = TileMap->GetTerra(); activeWorldSize = Vect2f(1.0f / terra->SizeX(), 1.0f / terra->SizeY()); @@ -270,25 +270,13 @@ void cSokolRender::SetTileColor(sColor4f color) { bool cSokolRender::CreateShadowTexture(int xysize) { DeleteShadowTexture(); - auto SetupTexture = [](SokolTexture2D *texture, sg_pixel_format pixel_format) { - auto description = texture->desc; - description->render_target = true; - description->usage = SG_USAGE_IMMUTABLE; - description->pixel_format = pixel_format; - SokolResourceKey resource_key = get_sokol_resource_key_texture( - description->width, description->height, description->pixel_format); - texture->image = new SokolResourceTexture(resource_key, sg_make_image(description)); - texture->resource_key = texture->image->key; - texture->dirty = false; - }; - shadowMapRenderTarget = new SokolRenderTarget{}; - shadowMapRenderTarget->texture = GetTexLibrary()->CreateRenderTexture(xysize, xysize, TEXTURE_RENDER16, false); + shadowMapRenderTarget->texture = GetTexLibrary()->CreateRenderTexture(xysize, xysize, TEXTURE_RENDER_DEPTH, false); if (!shadowMapRenderTarget->texture) { DeleteShadowTexture(); return false; } - SetupTexture(shadowMapRenderTarget->texture->GetFrameImage(0)->sg, SG_PIXELFORMAT_DEPTH); + PrepareSokolTexture(shadowMapRenderTarget->texture->GetFrameImage(0)->sg); { auto& render_pass = shadowMapRenderTarget->render_pass; @@ -307,8 +295,7 @@ bool cSokolRender::CreateShadowTexture(int xysize) { DeleteShadowTexture(); return false; } - SetupTexture(lightMapRenderTarget->texture->GetFrameImage(0)->sg, - sg_query_desc().environment.defaults.color_format); + PrepareSokolTexture(lightMapRenderTarget->texture->GetFrameImage(0)->sg); { auto& render_pass = lightMapRenderTarget->render_pass; diff --git a/Source/Render/sokol/SokolRenderState.cpp b/Source/Render/sokol/SokolRenderState.cpp index ce8eca16..6c35e8ef 100644 --- a/Source/Render/sokol/SokolRenderState.cpp +++ b/Source/Render/sokol/SokolRenderState.cpp @@ -97,9 +97,9 @@ void cSokolRender::DoSokolRendering() { } #endif - for (auto& target : std::array{shadowMapRenderTarget, lightMapRenderTarget}) { + for (auto& target : { shadowMapRenderTarget, lightMapRenderTarget }) { if (target != nullptr) { - DoSokolRendering(target->render_pass, target->commands); + ProcessRenderPass(target->render_pass, target->commands); } } @@ -117,14 +117,14 @@ void cSokolRender::DoSokolRendering() { swapchain_pass.action.stencil.store_action = SG_STOREACTION_DONTCARE; swapchain_pass.action.stencil.clear_value = 0; - DoSokolRendering(swapchain_pass, commands); + ProcessRenderPass(swapchain_pass, commands); } -void cSokolRender::DoSokolRendering(sg_pass& render_pass, const std::vector& commands) { +void cSokolRender::ProcessRenderPass(sg_pass& render_pass, const std::vector& pass_commands) { sg_begin_pass(&render_pass); //Iterate each command - for (auto& command : commands) { + for (auto& command : pass_commands) { #ifdef PERIMETER_RENDER_TRACKER_COMMANDS RenderSubmitEvent(RenderEvent::PROCESS_COMMAND, "", command); #endif @@ -152,7 +152,7 @@ void cSokolRender::DoSokolRendering(sg_pass& render_pass, const std::vectorpipeline; if (pipeline == nullptr) { //Not implemented vertex format - xxassert(0, "cSokolRender::EndScene missing pipeline"); + xxassert(0, "cSokolRender::ProcessRenderPass missing pipeline"); continue; } #if defined(PERIMETER_DEBUG) && 0 @@ -178,7 +178,7 @@ void cSokolRender::DoSokolRendering(sg_pass& render_pass, const std::vectorpipeline) != SG_RESOURCESTATE_VALID) { - xxassert(0, "cSokolRender::EndScene not valid state"); + xxassert(0, "cSokolRender::ProcessRenderPass not valid state"); continue; } sg_apply_pipeline(pipeline->pipeline); @@ -188,24 +188,24 @@ void cSokolRender::DoSokolRendering(sg_pass& render_pass, const std::vectorvertex_buffer) { - xxassert(0, "cSokolRender::EndScene missing vertex_buffer"); + xxassert(0, "cSokolRender::ProcessRenderPass missing vertex_buffer"); continue; } #ifdef PERIMETER_DEBUG if (sg_query_buffer_state(command->vertex_buffer->res) != SG_RESOURCESTATE_VALID) { - xxassert(0, "cSokolRender::EndScene vertex_buffer not valid state"); + xxassert(0, "cSokolRender::ProcessRenderPass vertex_buffer not valid state"); continue; } #endif bindings.vertex_buffers[0] = command->vertex_buffer->res; xassert(command->indices); if (!command->index_buffer) { - xxassert(0, "cSokolRender::EndScene missing index_buffer"); + xxassert(0, "cSokolRender::ProcessRenderPass missing index_buffer"); continue; } #ifdef PERIMETER_DEBUG if (sg_query_buffer_state(command->index_buffer->res) != SG_RESOURCESTATE_VALID) { - xxassert(0, "cSokolRender::EndScene index_buffer not valid state"); + xxassert(0, "cSokolRender::ProcessRenderPass index_buffer not valid state"); continue; } #endif @@ -221,14 +221,17 @@ void cSokolRender::DoSokolRendering(sg_pass& render_pass, const std::vectorshader_fs_texture_slot[i]; if (fs_slot < 0) continue; - SokolResourceTexture* tex = command->sokol_textures[i]; + SokolResourceImage* image = command->sokol_images[i]; + if (!image) { + image = emptyTexture->image; + } #ifdef PERIMETER_DEBUG - if (sg_query_image_state(tex->res) != SG_RESOURCESTATE_VALID) { - xxassert(0, "cSokolRender::EndScene sampler tex not valid state"); + if (sg_query_image_state(image->res) != SG_RESOURCESTATE_VALID) { + xxassert(0, "cSokolRender::ProcessRenderPass sampler image not valid state"); continue; } #endif - bindings.fs.images[fs_slot] = tex->res; + bindings.fs.images[fs_slot] = image->res; } sg_apply_bindings(&bindings); @@ -310,7 +313,7 @@ int cSokolRender::Flush(bool wnd) { #endif ClearPooledResources(MAX_POOLED_RESOURCES_LIFE); - ClearCommands(); + ClearAllCommands(); xassert(!activeDrawBuffer || !activeDrawBuffer->written_vertices); @@ -410,6 +413,10 @@ void cSokolRender::PrepareSokolBuffer(SokolBuffer*& buffer_ptr, MemoryResource* ); } else { buffer = nh.mapped().resource; + xassert(buffer->res.id != SG_INVALID_ID); + xassert(!buffer->burned); + xassert(buffer->pooled); + buffer->pooled = false; } resource->dirty = true; @@ -446,6 +453,72 @@ void cSokolRender::PrepareSokolBuffer(SokolBuffer*& buffer_ptr, MemoryResource* } } +void cSokolRender::PrepareSokolTexture(SokolTexture2D* tex) { + //Remove current image as is dirty + if (tex->dirty && tex->resource_key != SokolResourceKeyNone && tex->image) { + tex->image->DecRef(); + tex->image = nullptr; + } + + //Setup image resource + if (!tex->image) { + if (!tex->desc) { + xassert(tex->desc); + return; + } + + sg_image_desc*& desc = tex->desc; +#ifdef PERIMETER_DEBUG + if (!tex->label.empty()) { + desc->label = tex->label.c_str(); + } +#endif + if (desc->usage == SG_USAGE_IMMUTABLE) { + tex->resource_key = SokolResourceKeyNone; + } else { + xassert(tex->data); + tex->resource_key = get_sokol_resource_key_texture( + desc->width, + desc->height, + desc->pixel_format + ); + } + + //Get already created resource if exists or create + auto nh = imagePool.extract(tex->resource_key); + if (nh.empty()) { + tex->image = new SokolResourceImage( + tex->resource_key, + sg_make_image(desc) + ); + } else { + tex->image = nh.mapped().resource; + xassert(tex->image->res.id != SG_INVALID_ID); + xassert(!tex->image->burned); + xassert(tex->image->pooled); + tex->image->pooled = false; + } + + if (desc->usage == SG_USAGE_IMMUTABLE) { + tex->image->burned = true; + + //We no longer need desc or data as this is immutable + tex->FreeImages(); + tex->FreeData(); + + delete desc; + desc = nullptr; + } else { + tex->dirty = true; + } + } + + //Update the texture if is not immutable + if (tex->resource_key != SokolResourceKeyNone) { + tex->update(); + } +} + void cSokolRender::FinishActiveDrawBuffer() { MT_IS_GRAPH(); if (!activeDrawBuffer || !activeDrawBuffer->written_vertices) { @@ -498,15 +571,19 @@ void cSokolRender::CreateCommandEmpty() { label = "Submit" + " Vtxs: " + std::to_string(cmd->vertices) + " Idxs: " + std::to_string(cmd->indices) - + " Tex0: " + std::to_string(reinterpret_cast(cmd->sokol_textures[0])) - + " Tex1: " + std::to_string(reinterpret_cast(cmd->sokol_textures[1])); + + " Tex0: " + std::to_string(reinterpret_cast(cmd->sokol_images[0])) + + " Tex1: " + std::to_string(reinterpret_cast(cmd->sokol_images[1])); RenderSubmitEvent(RenderEvent::CREATE_COMMAND, label.c_str(), cmd); #endif } void cSokolRender::CreateCommand(VertexBuffer* vb, size_t vertices, IndexBuffer* ib, size_t indices) { xassert(ActiveScene); - xassert(vb); + if (!vb) { + //Never supposed to happenKenji Tsuruta + xassert(vb); + return; + } MT_IS_GRAPH(); if (0 == vertices) vertices = activeCommand.vertices; if (0 == indices) indices = activeCommand.indices; @@ -527,15 +604,6 @@ void cSokolRender::CreateCommand(VertexBuffer* vb, size_t vertices, IndexBuffer* RenderSubmitEvent(RenderEvent::CREATE_COMMAND, "Start"); #endif -#ifdef PERIMETER_DEBUG - if (vb->fmt & VERTEX_FMT_TEX1) { - xassert(activeCommand.sokol_textures[0]); - } - if (vb->fmt & VERTEX_FMT_TEX2) { - xassert(activeCommand.sokol_textures[1]); - } -#endif - //Update buffers if (vb->data && (vb->sg == nullptr || vb->dirty)) { size_t len = vertices * vb->VertexSize; @@ -557,12 +625,21 @@ void cSokolRender::CreateCommand(VertexBuffer* vb, size_t vertices, IndexBuffer* } xassert(activeCommand.vertices <= vertices); xassert((activeCommand.base_elements + activeCommand.indices) <= indices); + xassert(0 < activeCommand.vertices); //Create command to be send SokolCommand* cmd = new SokolCommand(); cmd->pipeline = pipeline; for (int i = 0; i < PERIMETER_SOKOL_TEXTURES; ++i) { - cmd->SetTexture(i, activeCommand.sokol_textures[i]); + SokolTexture2D* tex = activeCommandTextures[i]; + if (tex == nullptr) { + cmd->SetImage(i, nullptr); + } else { + if (!tex->image || tex->dirty) { + PrepareSokolTexture(tex); + } + cmd->SetImage(i, tex->image); + } } cmd->base_elements = activeCommand.base_elements; cmd->vertices = activeCommand.vertices; @@ -654,8 +731,8 @@ void cSokolRender::CreateCommand(VertexBuffer* vb, size_t vertices, IndexBuffer* label = "Submit" + " Vtxs: " + std::to_string(cmd->vertices) + " Idxs: " + std::to_string(cmd->indices) - + " Tex0: " + std::to_string(reinterpret_cast(cmd->sokol_textures[0])) - + " Tex1: " + std::to_string(reinterpret_cast(cmd->sokol_textures[1])); + + " Tex0: " + std::to_string(reinterpret_cast(cmd->sokol_images[0])) + + " Tex1: " + std::to_string(reinterpret_cast(cmd->sokol_images[1])); RenderSubmitEvent(RenderEvent::CREATE_COMMAND, label.c_str(), cmd); #endif } @@ -815,51 +892,10 @@ void cSokolRender::SetBlendState(eBlendMode blend) { void cSokolRender::SetTextureImage(uint32_t slot, TextureImage* texture_image) { xassert(slot < GetMaxTextureSlots()); - SokolTexture2D* tex = texture_image == nullptr ? emptyTexture : texture_image->sg; - if (!tex) { - xxassert(0, "cSokolRender::EndScene sampler tex missing"); - 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) { - tex->FreeImages(); - 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) { + SokolTexture2D* tex = texture_image != nullptr ? texture_image->sg : nullptr; + if (activeCommandTextures[slot] != tex) { FinishActiveDrawBuffer(); - activeCommand.SetTexture(slot, tex->image); + activeCommandTextures[slot] = tex; } } diff --git a/Source/Render/sokol/SokolRenderTexture.cpp b/Source/Render/sokol/SokolRenderTexture.cpp index c2fc61b5..e41c1be1 100644 --- a/Source/Render/sokol/SokolRenderTexture.cpp +++ b/Source/Render/sokol/SokolRenderTexture.cpp @@ -29,17 +29,51 @@ int cSokolRender::CreateTexture(cTexture* Texture, cFileImage* FileImage, bool e } sg_image_desc* desc = new sg_image_desc(); desc->label = nullptr; //Added later + desc->render_target = false; desc->width = dx; desc->height = dy; - desc->pixel_format = SG_PIXELFORMAT_RGBA8; desc->num_slices = 1; desc->num_mipmaps = std::min(static_cast(SG_MAX_MIPMAPS), Texture->GetNumberMipMap()); - if (!FileImage) { + switch (Texture->GetFmt()) { + case SURFMT_NUMBER: + default: + fprintf(stderr, + "cSokolRender::CreateTexture Unknown texture format: %" PRIu32 "\n", + static_cast(Texture->GetFmt()) + ); + desc->pixel_format = SG_PIXELFORMAT_RGBA8; + break; + case SURFMT_COLOR: + case SURFMT_COLORALPHA: + case SURFMT_COLOR32: + case SURFMT_COLORALPHA32: + case SURFMT_BUMP: + case SURFMT_GRAYALPHA: + case SURFMT_UV: + case SURFMT_U16V16: + desc->pixel_format = SG_PIXELFORMAT_RGBA8; + break; + case SURFMT_RENDERMAP16: + case SURFMT_RENDERMAP32: + desc->render_target = true; + desc->pixel_format = SG_PIXELFORMAT_RGBA8; + break; + case SURFMT_RENDERMAP_DEPTH: + desc->render_target = true; + desc->pixel_format = SG_PIXELFORMAT_DEPTH; + break; + } + + if (desc->render_target || FileImage) { + desc->usage = SG_USAGE_IMMUTABLE; + } else { desc->usage = SG_USAGE_STREAM; + } + + if (!FileImage) { img = new SokolTexture2D(desc); } else { - desc->usage = SG_USAGE_IMMUTABLE; uint8_t* buf = new uint8_t[tex_len]; memset(buf, 0xFF, tex_len); //Load in RGBA @@ -99,7 +133,7 @@ int cSokolRender::CreateTexture(cTexture* Texture, cFileImage* FileImage, bool e img = new SokolTexture2D(desc); #ifdef PERIMETER_DEBUG - img->label = Texture->GetName() + std::to_string(i); + img->label = std::string(Texture->GetName()) + '_' + std::to_string(i); #endif } @@ -191,5 +225,6 @@ void cSokolRender::UnlockTexture(cTexture* Texture) { } SurfaceImage cSokolRender::GetShadowZBuffer() { + //TODO return SurfaceImage::NONE; } diff --git a/Source/Render/sokol/SokolResources.cpp b/Source/Render/sokol/SokolResources.cpp index fde81816..163d94e1 100644 --- a/Source/Render/sokol/SokolResources.cpp +++ b/Source/Render/sokol/SokolResources.cpp @@ -4,32 +4,54 @@ #include "SokolResources.h" #include "SokolTypes.h" +static const uint32_t CHUNK_LEN_SHIFT = 6; //64 bytes + +template +uint16_t get_chunk_amount(T& len) { + if (len < 0) { + xassert(0); + return 0; + } + size_t chunks = (len >> CHUNK_LEN_SHIFT) + 1; //Divide by LEN_SHIFT and add one extra chunk + len = chunks * (1 << CHUNK_LEN_SHIFT); + xassert(chunks <= 0xFFFF); + return static_cast(chunks); +} + SokolResourceKey get_sokol_resource_key_buffer(size_t& len, sg_buffer_type type) { - static const uint32_t LEN_SHIFT = 6; //64 bytes - size_t chunks = (len >> LEN_SHIFT) + 1; //Divide by LEN_SHIFT and add one extra chunk - len = chunks * (1 << LEN_SHIFT); - SokolResourceKey key = len; + SokolResourceKey key = get_chunk_amount(len); key <<= 16; key |= static_cast(type & 0xFFFF); return key; } SokolResourceKey get_sokol_resource_key_texture(int& w, int& h, sg_pixel_format format) { - SokolResourceKey len = static_cast(w); - len <<= 32; - len |= static_cast(h); - len <<= 8; - len |= static_cast(sokol_pixelformat_bytesize(format) & 0xFF); - return len; + xassert(0 < w && w <= 0xFFFF); + xassert(0 < h && h <= 0xFFFF); + SokolResourceKey key = static_cast(w); + key <<= 16; + key |= static_cast(h); + key <<= 16; + key |= static_cast(format & 0xFFFF); + return key; } size_t sokol_pixelformat_bytesize(sg_pixel_format fmt) { - //Probably the only pixel format used, so we cache it - if (fmt == SG_PIXELFORMAT_RGBA8) { - static int rgba8 = sg_query_pixelformat(fmt).bytes_per_pixel; - return rgba8; + //Probably the only pixel formats used, so we cache it + switch (fmt) { + case SG_PIXELFORMAT_RGBA8: { + static int bpp_rgba8 = sg_query_pixelformat(fmt).bytes_per_pixel; + return bpp_rgba8; + } + case SG_PIXELFORMAT_DEPTH: { + static int bpp_depth = sg_query_pixelformat(fmt).bytes_per_pixel; + return bpp_depth; + } + default: { + xassert(0); + return sg_query_pixelformat(fmt).bytes_per_pixel; + } } - return sg_query_pixelformat(fmt).bytes_per_pixel; } template<> @@ -41,7 +63,7 @@ void SokolResourceBuffer::destroy_res() { } template<> -void SokolResourceTexture::destroy_res() { +void SokolResourceImage::destroy_res() { if (res.id != SG_INVALID_ID) { sg_destroy_image(res); res.id = SG_INVALID_ID; diff --git a/Source/Render/sokol/SokolResources.h b/Source/Render/sokol/SokolResources.h index e6d1348c..150aa4e5 100644 --- a/Source/Render/sokol/SokolResources.h +++ b/Source/Render/sokol/SokolResources.h @@ -38,6 +38,7 @@ class SokolResource { } } public: + bool pooled = false; bool burned = false; SokolResourceKey key = SokolResourceKeyNone; T res; diff --git a/Source/Render/sokol/SokolTypes.h b/Source/Render/sokol/SokolTypes.h index b2c713ff..38b031d5 100644 --- a/Source/Render/sokol/SokolTypes.h +++ b/Source/Render/sokol/SokolTypes.h @@ -47,7 +47,7 @@ struct SokolPipelineMode { }; template class SokolResource; -using SokolResourceTexture = SokolResource; +using SokolResourceImage = SokolResource; using SokolResourceBuffer = SokolResource; #endif //PERIMETER_SOKOLTYPES_H diff --git a/Source/Render/src/texture.inl b/Source/Render/src/texture.inl index c8f5ff46..3c997191 100644 --- a/Source/Render/src/texture.inl +++ b/Source/Render/src/texture.inl @@ -12,7 +12,7 @@ enum eAttributeTexture TEXTURE_POOL_DEFAULT = 1<<26, TEXTURE_GRAY = 1<<27, TEXTURE_UVBUMP = 1<<28, - TEXTURE_U16V16 = 1<<29, + TEXTURE_U16V16 = 1<<29, TEXTURE_ALPHA_BLEND = MAT_ALPHA_BLEND, // текстура содержит альфу TEXTURE_ALPHA_TEST = MAT_ALPHA_TEST, // текстура содержит маску в альфе