mirror of
https://github.com/TombEngine/TombEngine.git
synced 2025-04-28 15:57:59 +03:00
Multithreading (#1541)
* Create Worker class for multithreading * Update TombEngine.vcxproj * Rename GetWorkerCount() to GetThreadCount() * Add ProcessInParallel template for vectors * Add multiThreaded flag to settings, process sprites in parallel * Update Flow.Settings.html * Refine WorkerManager class conventions; deinit threads properly * Don't require explicit destruction * Address basic PR notes * Update Worker.cpp * Simplify ThreadManager class * Add method for running single task * Use singleton pattern; use more appropriate Controller suffix * Update WorkerController template method * Revise method * Handle exception in ~WorkerController() * Grammar * Correctly init single-threaded mode * Update CHANGELOG.md * Defer thread init until g_GameFlow is valid * unsigned int -> int * Rename class --------- Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
This commit is contained in:
parent
d37ac17a39
commit
ca56f62f54
13 changed files with 325 additions and 52 deletions
|
@ -49,6 +49,7 @@ TombEngine releases are located in this repository (alongside with Tomb Editor):
|
||||||
* Fixed gravity being applied when vaulting on the same frame as the player lands.
|
* Fixed gravity being applied when vaulting on the same frame as the player lands.
|
||||||
|
|
||||||
### New Features
|
### New Features
|
||||||
|
* Added multithreading setting to flow system settings.
|
||||||
* Added realtime shader reloading in debug mode by pressing F9 key.
|
* Added realtime shader reloading in debug mode by pressing F9 key.
|
||||||
* Added load, save, stopwatch and compass as a functional pick-up items with ability to add or remove them from inventory.
|
* Added load, save, stopwatch and compass as a functional pick-up items with ability to add or remove them from inventory.
|
||||||
* Increased particle limit from 1024 to 4096.
|
* Increased particle limit from 1024 to 4096.
|
||||||
|
|
|
@ -305,6 +305,10 @@
|
||||||
<td class="summary">How should the application respond to script errors?</td>
|
<td class="summary">How should the application respond to script errors?</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td class="name" ><a href="#multiThreaded">multiThreaded</a></td>
|
||||||
|
<td class="summary">Use multithreading in certain calculations.</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
<td class="name" ><a href="#fastReload">fastReload</a></td>
|
<td class="name" ><a href="#fastReload">fastReload</a></td>
|
||||||
<td class="summary">Can the game utilize the fast reload feature?</td>
|
<td class="summary">Can the game utilize the fast reload feature?</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -1253,6 +1257,29 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</dd>
|
||||||
|
<dt>
|
||||||
|
<a name = "multiThreaded"></a>
|
||||||
|
<strong>multiThreaded</strong>
|
||||||
|
</dt>
|
||||||
|
<dd>
|
||||||
|
Use multithreading in certain calculations. <br>
|
||||||
|
When set to <code>true</code>, some performance-critical calculations will be performed in parallel, which can give
|
||||||
|
a significant performance boost. Don't disable unless you have problems with launching or using TombEngine.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
<li><span class="parameter">multiThreaded</span>
|
||||||
|
<span class="types"><span class="type">bool</span></span>
|
||||||
|
determines whether to use multithreading or not. */
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</dd>
|
</dd>
|
||||||
<dt>
|
<dt>
|
||||||
<a name = "fastReload"></a>
|
<a name = "fastReload"></a>
|
||||||
|
|
|
@ -509,7 +509,7 @@ namespace TEN::Renderer
|
||||||
const Vector4& color0, const Vector4& color1, const Vector4& color2, const Vector4& color3,
|
const Vector4& color0, const Vector4& color1, const Vector4& color2, const Vector4& color3,
|
||||||
BlendMode blendMode, RenderView& view, SpriteRenderType renderType = SpriteRenderType::Default);
|
BlendMode blendMode, RenderView& view, SpriteRenderType renderType = SpriteRenderType::Default);
|
||||||
|
|
||||||
Matrix GetWorldMatrixForSprite(RendererSpriteToDraw* spr, RenderView& view);
|
Matrix GetWorldMatrixForSprite(const RendererSpriteToDraw& sprite, RenderView& view);
|
||||||
RendererObject& GetRendererObject(GAME_OBJECT_ID id);
|
RendererObject& GetRendererObject(GAME_OBJECT_ID id);
|
||||||
RendererMesh* GetMesh(int meshIndex);
|
RendererMesh* GetMesh(int meshIndex);
|
||||||
Texture2D CreateDefaultNormalTexture();
|
Texture2D CreateDefaultNormalTexture();
|
||||||
|
|
|
@ -2925,7 +2925,7 @@ namespace TEN::Renderer
|
||||||
rDrawSprite.Width = STAR_SIZE * star.Scale;
|
rDrawSprite.Width = STAR_SIZE * star.Scale;
|
||||||
rDrawSprite.Height = STAR_SIZE * star.Scale;
|
rDrawSprite.Height = STAR_SIZE * star.Scale;
|
||||||
|
|
||||||
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(&rDrawSprite, renderView);
|
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(rDrawSprite, renderView);
|
||||||
_stInstancedSpriteBuffer.Sprites[i].Color = Vector4(
|
_stInstancedSpriteBuffer.Sprites[i].Color = Vector4(
|
||||||
star.Color.x,
|
star.Color.x,
|
||||||
star.Color.y,
|
star.Color.y,
|
||||||
|
@ -2988,7 +2988,7 @@ namespace TEN::Renderer
|
||||||
rDrawSprite.Height = 192;
|
rDrawSprite.Height = 192;
|
||||||
rDrawSprite.ConstrainAxis = meteor.Direction;
|
rDrawSprite.ConstrainAxis = meteor.Direction;
|
||||||
|
|
||||||
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(&rDrawSprite, renderView);
|
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(rDrawSprite, renderView);
|
||||||
_stInstancedSpriteBuffer.Sprites[i].Color = Vector4(
|
_stInstancedSpriteBuffer.Sprites[i].Color = Vector4(
|
||||||
meteor.Color.x,
|
meteor.Color.x,
|
||||||
meteor.Color.y,
|
meteor.Color.y,
|
||||||
|
@ -3092,7 +3092,7 @@ namespace TEN::Renderer
|
||||||
rDrawSprite.Height = SUN_SIZE;
|
rDrawSprite.Height = SUN_SIZE;
|
||||||
rDrawSprite.color = renderView.LensFlaresToDraw[0].Color;
|
rDrawSprite.color = renderView.LensFlaresToDraw[0].Color;
|
||||||
|
|
||||||
_stInstancedSpriteBuffer.Sprites[0].World = GetWorldMatrixForSprite(&rDrawSprite, renderView);
|
_stInstancedSpriteBuffer.Sprites[0].World = GetWorldMatrixForSprite(rDrawSprite, renderView);
|
||||||
_stInstancedSpriteBuffer.Sprites[0].Color = renderView.LensFlaresToDraw[0].Color;
|
_stInstancedSpriteBuffer.Sprites[0].Color = renderView.LensFlaresToDraw[0].Color;
|
||||||
_stInstancedSpriteBuffer.Sprites[0].IsBillboard = 1;
|
_stInstancedSpriteBuffer.Sprites[0].IsBillboard = 1;
|
||||||
_stInstancedSpriteBuffer.Sprites[0].IsSoftParticle = 0;
|
_stInstancedSpriteBuffer.Sprites[0].IsSoftParticle = 0;
|
||||||
|
@ -3442,7 +3442,7 @@ namespace TEN::Renderer
|
||||||
uv2 = spr->Sprite->UV[2];
|
uv2 = spr->Sprite->UV[2];
|
||||||
uv3 = spr->Sprite->UV[3];
|
uv3 = spr->Sprite->UV[3];
|
||||||
|
|
||||||
auto world = GetWorldMatrixForSprite(currentObject->Sprite, view);
|
auto world = GetWorldMatrixForSprite(*currentObject->Sprite, view);
|
||||||
|
|
||||||
Vertex v0;
|
Vertex v0;
|
||||||
v0.Position = Vector3::Transform(p0t, world);
|
v0.Position = Vector3::Transform(p0t, world);
|
||||||
|
|
|
@ -1291,14 +1291,14 @@ namespace TEN::Renderer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Matrix Renderer::GetWorldMatrixForSprite(RendererSpriteToDraw* sprite, RenderView& view)
|
Matrix Renderer::GetWorldMatrixForSprite(const RendererSpriteToDraw& sprite, RenderView& view)
|
||||||
{
|
{
|
||||||
auto spriteMatrix = Matrix::Identity;
|
auto spriteMatrix = Matrix::Identity;
|
||||||
auto scaleMatrix = Matrix::CreateScale(sprite->Width * sprite->Scale, sprite->Height * sprite->Scale, sprite->Scale);
|
auto scaleMatrix = Matrix::CreateScale(sprite.Width * sprite.Scale, sprite.Height * sprite.Scale, sprite.Scale);
|
||||||
|
|
||||||
auto spritePos = sprite->pos;
|
auto spritePos = sprite.pos;
|
||||||
|
|
||||||
if (sprite->Type == SpriteType::ThreeD)
|
if (sprite.Type == SpriteType::ThreeD)
|
||||||
{
|
{
|
||||||
ReflectMatrixOptionally(spriteMatrix);
|
ReflectMatrixOptionally(spriteMatrix);
|
||||||
}
|
}
|
||||||
|
@ -1307,23 +1307,23 @@ namespace TEN::Renderer
|
||||||
ReflectVectorOptionally(spritePos);
|
ReflectVectorOptionally(spritePos);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (sprite->Type)
|
switch (sprite.Type)
|
||||||
{
|
{
|
||||||
case SpriteType::Billboard:
|
case SpriteType::Billboard:
|
||||||
{
|
{
|
||||||
auto cameraUp = Vector3(view.Camera.View._12, view.Camera.View._22, view.Camera.View._32);
|
auto cameraUp = Vector3(view.Camera.View._12, view.Camera.View._22, view.Camera.View._32);
|
||||||
spriteMatrix = scaleMatrix * Matrix::CreateRotationZ(sprite->Rotation) * Matrix::CreateBillboard(spritePos, Camera.pos.ToVector3(), cameraUp);
|
spriteMatrix = scaleMatrix * Matrix::CreateRotationZ(sprite.Rotation) * Matrix::CreateBillboard(spritePos, Camera.pos.ToVector3(), cameraUp);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SpriteType::CustomBillboard:
|
case SpriteType::CustomBillboard:
|
||||||
{
|
{
|
||||||
auto rotMatrix = Matrix::CreateRotationY(sprite->Rotation);
|
auto rotMatrix = Matrix::CreateRotationY(sprite.Rotation);
|
||||||
auto quadForward = Vector3(0.0f, 0.0f, 1.0f);
|
auto quadForward = Vector3(0.0f, 0.0f, 1.0f);
|
||||||
spriteMatrix = scaleMatrix * Matrix::CreateConstrainedBillboard(
|
spriteMatrix = scaleMatrix * Matrix::CreateConstrainedBillboard(
|
||||||
spritePos,
|
spritePos,
|
||||||
Camera.pos.ToVector3(),
|
Camera.pos.ToVector3(),
|
||||||
sprite->ConstrainAxis,
|
sprite.ConstrainAxis,
|
||||||
nullptr,
|
nullptr,
|
||||||
&quadForward);
|
&quadForward);
|
||||||
}
|
}
|
||||||
|
@ -1332,7 +1332,7 @@ namespace TEN::Renderer
|
||||||
case SpriteType::LookAtBillboard:
|
case SpriteType::LookAtBillboard:
|
||||||
{
|
{
|
||||||
auto translationMatrix = Matrix::CreateTranslation(spritePos);
|
auto translationMatrix = Matrix::CreateTranslation(spritePos);
|
||||||
auto rotMatrix = Matrix::CreateRotationZ(sprite->Rotation) * Matrix::CreateLookAt(Vector3::Zero, sprite->LookAtAxis, Vector3::UnitZ);
|
auto rotMatrix = Matrix::CreateRotationZ(sprite.Rotation) * Matrix::CreateLookAt(Vector3::Zero, sprite.LookAtAxis, Vector3::UnitZ);
|
||||||
spriteMatrix = scaleMatrix * rotMatrix * translationMatrix;
|
spriteMatrix = scaleMatrix * rotMatrix * translationMatrix;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1,14 +1,16 @@
|
||||||
#include "framework.h"
|
#include "framework.h"
|
||||||
#include "Renderer/Structures/RendererSprite.h"
|
#include "Renderer/Structures/RendererSprite.h"
|
||||||
|
|
||||||
#include "Renderer/Structures/RendererSpriteBucket.h"
|
#include "Renderer/Structures/RendererSpriteBucket.h"
|
||||||
#include "Renderer/Renderer.h"
|
#include "Renderer/Renderer.h"
|
||||||
|
#include "Specific/Parallel.h"
|
||||||
|
|
||||||
|
using namespace TEN::Renderer::Structures;
|
||||||
|
|
||||||
namespace TEN::Renderer
|
namespace TEN::Renderer
|
||||||
{
|
{
|
||||||
using namespace TEN::Renderer::Structures;
|
|
||||||
|
|
||||||
void Renderer::AddSpriteBillboard(RendererSprite* sprite, const Vector3& pos, const Vector4& color, float orient2D, float scale,
|
void Renderer::AddSpriteBillboard(RendererSprite* sprite, const Vector3& pos, const Vector4& color, float orient2D, float scale,
|
||||||
Vector2 size, BlendMode blendMode, bool isSoftParticle, RenderView& view, SpriteRenderType renderType)
|
Vector2 size, BlendMode blendMode, bool isSoftParticle, RenderView& view, SpriteRenderType renderType)
|
||||||
{
|
{
|
||||||
if (scale <= 0.0f)
|
if (scale <= 0.0f)
|
||||||
scale = 1.0f;
|
scale = 1.0f;
|
||||||
|
@ -38,8 +40,8 @@ namespace TEN::Renderer
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::AddSpriteBillboardConstrained(RendererSprite* sprite, const Vector3& pos, const Vector4& color, float orient2D,
|
void Renderer::AddSpriteBillboardConstrained(RendererSprite* sprite, const Vector3& pos, const Vector4& color, float orient2D,
|
||||||
float scale, Vector2 size, BlendMode blendMode, const Vector3& constrainAxis,
|
float scale, Vector2 size, BlendMode blendMode, const Vector3& constrainAxis,
|
||||||
bool isSoftParticle, RenderView& view, SpriteRenderType renderType)
|
bool isSoftParticle, RenderView& view, SpriteRenderType renderType)
|
||||||
{
|
{
|
||||||
if (scale <= 0.0f)
|
if (scale <= 0.0f)
|
||||||
scale = 1.0f;
|
scale = 1.0f;
|
||||||
|
@ -261,16 +263,16 @@ namespace TEN::Renderer
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Draw instanced sprites.
|
// Draw instanced sprites.
|
||||||
bool wasGPUSet = false;
|
bool wasGpuSet = false;
|
||||||
for (auto& spriteBucket : _spriteBuckets)
|
for (const auto& spriteBucket : _spriteBuckets)
|
||||||
{
|
{
|
||||||
if (spriteBucket.SpritesToDraw.size() == 0 || !spriteBucket.IsBillboard)
|
if (spriteBucket.SpritesToDraw.empty() || !spriteBucket.IsBillboard)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!SetupBlendModeAndAlphaTest(spriteBucket.BlendMode, rendererPass, 0))
|
if (!SetupBlendModeAndAlphaTest(spriteBucket.BlendMode, rendererPass, 0))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!wasGPUSet)
|
if (!wasGpuSet)
|
||||||
{
|
{
|
||||||
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
||||||
|
|
||||||
|
@ -286,42 +288,45 @@ namespace TEN::Renderer
|
||||||
unsigned int offset = 0;
|
unsigned int offset = 0;
|
||||||
_context->IASetVertexBuffers(0, 1, _quadVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
_context->IASetVertexBuffers(0, 1, _quadVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
||||||
|
|
||||||
wasGPUSet = true;
|
wasGpuSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare constant buffer for instanced sprites.
|
// Define sprite preparation logic.
|
||||||
for (int i = 0; i < spriteBucket.SpritesToDraw.size(); i++)
|
auto prepareSprites = [&](int start, int end)
|
||||||
{
|
{
|
||||||
auto& rDrawSprite = spriteBucket.SpritesToDraw[i];
|
for (int i = start; i < end; i++)
|
||||||
|
{
|
||||||
|
const auto& spriteToDraw = spriteBucket.SpritesToDraw[i];
|
||||||
|
|
||||||
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(&rDrawSprite, view);
|
_stInstancedSpriteBuffer.Sprites[i].World = GetWorldMatrixForSprite(spriteToDraw, view);
|
||||||
_stInstancedSpriteBuffer.Sprites[i].Color = rDrawSprite.color;
|
_stInstancedSpriteBuffer.Sprites[i].Color = spriteToDraw.color;
|
||||||
_stInstancedSpriteBuffer.Sprites[i].IsBillboard = 1;
|
_stInstancedSpriteBuffer.Sprites[i].IsBillboard = 1.0f;
|
||||||
_stInstancedSpriteBuffer.Sprites[i].IsSoftParticle = rDrawSprite.SoftParticle ? 1 : 0;
|
_stInstancedSpriteBuffer.Sprites[i].IsSoftParticle = spriteToDraw.SoftParticle ? 1.0f : 0.0f;
|
||||||
|
|
||||||
// NOTE: Strange packing due to particular HLSL 16 byte alignment requirements.
|
// NOTE: Strange packing due to particular HLSL 16 byte alignment requirements.
|
||||||
_stInstancedSpriteBuffer.Sprites[i].UV[0].x = rDrawSprite.Sprite->UV[0].x;
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].x = spriteToDraw.Sprite->UV[0].x;
|
||||||
_stInstancedSpriteBuffer.Sprites[i].UV[0].y = rDrawSprite.Sprite->UV[1].x;
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].y = spriteToDraw.Sprite->UV[1].x;
|
||||||
_stInstancedSpriteBuffer.Sprites[i].UV[0].z = rDrawSprite.Sprite->UV[2].x;
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].z = spriteToDraw.Sprite->UV[2].x;
|
||||||
_stInstancedSpriteBuffer.Sprites[i].UV[0].w = rDrawSprite.Sprite->UV[3].x;
|
_stInstancedSpriteBuffer.Sprites[i].UV[0].w = spriteToDraw.Sprite->UV[3].x;
|
||||||
_stInstancedSpriteBuffer.Sprites[i].UV[1].x = rDrawSprite.Sprite->UV[0].y;
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].x = spriteToDraw.Sprite->UV[0].y;
|
||||||
_stInstancedSpriteBuffer.Sprites[i].UV[1].y = rDrawSprite.Sprite->UV[1].y;
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].y = spriteToDraw.Sprite->UV[1].y;
|
||||||
_stInstancedSpriteBuffer.Sprites[i].UV[1].z = rDrawSprite.Sprite->UV[2].y;
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].z = spriteToDraw.Sprite->UV[2].y;
|
||||||
_stInstancedSpriteBuffer.Sprites[i].UV[1].w = rDrawSprite.Sprite->UV[3].y;
|
_stInstancedSpriteBuffer.Sprites[i].UV[1].w = spriteToDraw.Sprite->UV[3].y;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
g_Parallel.AddTasks((int)spriteBucket.SpritesToDraw.size(), prepareSprites).wait();
|
||||||
|
|
||||||
BindTexture(TextureRegister::ColorMap, spriteBucket.Sprite->Texture, SamplerStateRegister::LinearClamp);
|
BindTexture(TextureRegister::ColorMap, spriteBucket.Sprite->Texture, SamplerStateRegister::LinearClamp);
|
||||||
|
|
||||||
_cbInstancedSpriteBuffer.UpdateData(_stInstancedSpriteBuffer, _context.Get());
|
_cbInstancedSpriteBuffer.UpdateData(_stInstancedSpriteBuffer, _context.Get());
|
||||||
|
|
||||||
// Draw sprites with instancing.
|
// Draw sprites with instancing.
|
||||||
DrawInstancedTriangles(4, (unsigned int)spriteBucket.SpritesToDraw.size(), 0);
|
DrawInstancedTriangles(4, (int)spriteBucket.SpritesToDraw.size(), 0);
|
||||||
|
|
||||||
_numInstancedSpritesDrawCalls++;
|
_numInstancedSpritesDrawCalls++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw 3D non-instanced sprites.
|
// Draw 3D non-instanced sprites.
|
||||||
wasGPUSet = false;
|
wasGpuSet = false;
|
||||||
|
|
||||||
for (auto& spriteBucket : _spriteBuckets)
|
for (auto& spriteBucket : _spriteBuckets)
|
||||||
{
|
{
|
||||||
|
@ -331,7 +336,7 @@ namespace TEN::Renderer
|
||||||
if (!SetupBlendModeAndAlphaTest(spriteBucket.BlendMode, rendererPass, 0))
|
if (!SetupBlendModeAndAlphaTest(spriteBucket.BlendMode, rendererPass, 0))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!wasGPUSet)
|
if (!wasGpuSet)
|
||||||
{
|
{
|
||||||
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
||||||
|
|
||||||
|
@ -342,10 +347,10 @@ namespace TEN::Renderer
|
||||||
|
|
||||||
_shaders.Bind(Shader::Sprites);
|
_shaders.Bind(Shader::Sprites);
|
||||||
|
|
||||||
wasGPUSet = true;
|
wasGpuSet = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stSprite.IsSoftParticle = spriteBucket.IsSoftParticle ? 1 : 0;
|
_stSprite.IsSoftParticle = spriteBucket.IsSoftParticle ? 1.0f : 0.0f;
|
||||||
_stSprite.RenderType = (int)spriteBucket.RenderType;
|
_stSprite.RenderType = (int)spriteBucket.RenderType;
|
||||||
_cbSprite.UpdateData(_stSprite, _context.Get());
|
_cbSprite.UpdateData(_stSprite, _context.Get());
|
||||||
|
|
||||||
|
@ -414,11 +419,11 @@ namespace TEN::Renderer
|
||||||
_shaders.Bind(Shader::InstancedSprites);
|
_shaders.Bind(Shader::InstancedSprites);
|
||||||
|
|
||||||
// Set up vertex buffer and parameters.
|
// Set up vertex buffer and parameters.
|
||||||
UINT stride = sizeof(Vertex);
|
unsigned int stride = sizeof(Vertex);
|
||||||
UINT offset = 0;
|
unsigned int offset = 0;
|
||||||
_context->IASetVertexBuffers(0, 1, _quadVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
_context->IASetVertexBuffers(0, 1, _quadVertexBuffer.Buffer.GetAddressOf(), &stride, &offset);
|
||||||
|
|
||||||
_stInstancedSpriteBuffer.Sprites[0].World = GetWorldMatrixForSprite(object->Sprite, view);
|
_stInstancedSpriteBuffer.Sprites[0].World = GetWorldMatrixForSprite(*object->Sprite, view);
|
||||||
_stInstancedSpriteBuffer.Sprites[0].Color = object->Sprite->color;
|
_stInstancedSpriteBuffer.Sprites[0].Color = object->Sprite->color;
|
||||||
_stInstancedSpriteBuffer.Sprites[0].IsBillboard = 1;
|
_stInstancedSpriteBuffer.Sprites[0].IsBillboard = 1;
|
||||||
_stInstancedSpriteBuffer.Sprites[0].IsSoftParticle = object->Sprite->SoftParticle ? 1 : 0;
|
_stInstancedSpriteBuffer.Sprites[0].IsSoftParticle = object->Sprite->SoftParticle ? 1 : 0;
|
||||||
|
|
|
@ -328,6 +328,12 @@ namespace TEN::Scripting
|
||||||
// @tfield Flow.ErrorMode errorMode error mode to use. */
|
// @tfield Flow.ErrorMode errorMode error mode to use. */
|
||||||
"errorMode", &SystemSettings::ErrorMode,
|
"errorMode", &SystemSettings::ErrorMode,
|
||||||
|
|
||||||
|
/// Use multithreading in certain calculations. <br>
|
||||||
|
// When set to `true`, some performance-critical calculations will be performed in parallel, which can give
|
||||||
|
// a significant performance boost. Don't disable unless you have problems with launching or using TombEngine.
|
||||||
|
// @tfield bool multithreaded determines whether to use multithreading or not. */
|
||||||
|
"multithreaded", &SystemSettings::Multithreaded,
|
||||||
|
|
||||||
/// Can the game utilize the fast reload feature? <br>
|
/// Can the game utilize the fast reload feature? <br>
|
||||||
// When set to `true`, the game will attempt to perform fast savegame reloading if current level is the same as
|
// When set to `true`, the game will attempt to perform fast savegame reloading if current level is the same as
|
||||||
// the level loaded from the savegame. It will not work if the level timestamp or checksum has changed
|
// the level loaded from the savegame. It will not work if the level timestamp or checksum has changed
|
||||||
|
|
|
@ -78,8 +78,9 @@ namespace TEN::Scripting
|
||||||
|
|
||||||
struct SystemSettings
|
struct SystemSettings
|
||||||
{
|
{
|
||||||
ErrorMode ErrorMode = ErrorMode::Warn;
|
ErrorMode ErrorMode = ErrorMode::Warn;
|
||||||
bool FastReload = true;
|
bool FastReload = true;
|
||||||
|
bool Multithreaded = true;
|
||||||
|
|
||||||
static void Register(sol::table& parent);
|
static void Register(sol::table& parent);
|
||||||
};
|
};
|
||||||
|
|
173
TombEngine/Specific/Parallel.cpp
Normal file
173
TombEngine/Specific/Parallel.cpp
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
#include "framework.h"
|
||||||
|
#include "Specific/Parallel.h"
|
||||||
|
|
||||||
|
#include "Scripting/Include/Flow/ScriptInterfaceFlowHandler.h"
|
||||||
|
|
||||||
|
namespace TEN::Utils
|
||||||
|
{
|
||||||
|
ParallelTaskManager& g_Parallel = ParallelTaskManager::Get();
|
||||||
|
|
||||||
|
ParallelTaskManager::ParallelTaskManager()
|
||||||
|
{
|
||||||
|
_deinitialize = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelTaskManager::~ParallelTaskManager()
|
||||||
|
{
|
||||||
|
// LOCK: Restrict shutdown flag access.
|
||||||
|
{
|
||||||
|
auto taskLock = std::lock_guard(_taskMutex);
|
||||||
|
|
||||||
|
_deinitialize = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify all threads they should stop.
|
||||||
|
_taskCond.notify_all();
|
||||||
|
|
||||||
|
// Join all threads.
|
||||||
|
for (auto& thread : _threads)
|
||||||
|
{
|
||||||
|
if (!thread.joinable())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
catch (const std::exception& ex)
|
||||||
|
{
|
||||||
|
TENLog("Error joining thread: " + std::string(ex.what()), LogLevel::Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ParallelTaskManager& ParallelTaskManager::Get()
|
||||||
|
{
|
||||||
|
static auto instance = ParallelTaskManager();
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ParallelTaskManager::GetThreadCount() const
|
||||||
|
{
|
||||||
|
return (unsigned int)_threads.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int ParallelTaskManager::GetCoreCount() const
|
||||||
|
{
|
||||||
|
return std::max(std::thread::hardware_concurrency(), 1u);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParallelTaskManager::Initialize()
|
||||||
|
{
|
||||||
|
// Reserve threads.
|
||||||
|
unsigned int threadCount = g_GameFlow->GetSettings()->System.Multithreaded ? (GetCoreCount() * 2) : 1;
|
||||||
|
_threads.reserve(threadCount);
|
||||||
|
|
||||||
|
// Create threads.
|
||||||
|
for (int i = 0; i < threadCount; i++)
|
||||||
|
_threads.push_back(std::thread(&ParallelTaskManager::Worker, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<void> ParallelTaskManager::AddTask(const ParallelTask& task)
|
||||||
|
{
|
||||||
|
return AddTasks(ParallelTaskGroup{ task });
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<void> ParallelTaskManager::AddTasks(const ParallelTaskGroup& tasks)
|
||||||
|
{
|
||||||
|
// HEAP ALLOC: Create counter and promise.
|
||||||
|
auto counter = std::make_shared<std::atomic<int>>();
|
||||||
|
auto promise = std::make_shared<std::promise<void>>();
|
||||||
|
|
||||||
|
counter->store((int)tasks.size(), std::memory_order_release);
|
||||||
|
|
||||||
|
// Add group tasks.
|
||||||
|
for (const auto& task : tasks)
|
||||||
|
AddTask(task, counter, promise);
|
||||||
|
|
||||||
|
// Notify available threads to handle tasks.
|
||||||
|
_taskCond.notify_all();
|
||||||
|
|
||||||
|
// Return future to wait on task group completion if needed.
|
||||||
|
return promise->get_future();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::future<void> ParallelTaskManager::AddTasks(int elementCount, const std::function<void(int, int)>& splitTask)
|
||||||
|
{
|
||||||
|
// TODO: Make this a configuration option?
|
||||||
|
constexpr auto SERIAL_UNIT_COUNT_MAX = 32;
|
||||||
|
|
||||||
|
auto tasks = ParallelTaskGroup{};
|
||||||
|
|
||||||
|
// Process in parallel.
|
||||||
|
if (g_GameFlow->GetSettings()->System.Multithreaded &&
|
||||||
|
elementCount > SERIAL_UNIT_COUNT_MAX)
|
||||||
|
{
|
||||||
|
int threadCount = GetCoreCount();
|
||||||
|
int chunkSize = ((elementCount + threadCount) - 1) / threadCount;
|
||||||
|
|
||||||
|
// Collect group tasks.
|
||||||
|
tasks.reserve(threadCount);
|
||||||
|
for (int i = 0; i < threadCount; i++)
|
||||||
|
{
|
||||||
|
int start = i * chunkSize;
|
||||||
|
int end = std::min(start + chunkSize, elementCount);
|
||||||
|
tasks.push_back([&splitTask, start, end]() { splitTask(start, end); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Process linearly.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tasks.push_back([&splitTask, elementCount]() { splitTask(0, elementCount); });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add task group and return future to wait on completion if needed.
|
||||||
|
return AddTasks(tasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParallelTaskManager::Worker()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto task = ParallelTask();
|
||||||
|
|
||||||
|
// LOCK: Restrict task queue access.
|
||||||
|
{
|
||||||
|
auto taskLock = std::unique_lock(_taskMutex);
|
||||||
|
_taskCond.wait(taskLock, [this] { return (_deinitialize || !_tasks.empty()); });
|
||||||
|
|
||||||
|
// Shutting down and no pending tasks; return early.
|
||||||
|
if (_deinitialize && _tasks.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Get task.
|
||||||
|
task = _tasks.front();
|
||||||
|
_tasks.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute task.
|
||||||
|
if (task)
|
||||||
|
task();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParallelTaskManager::AddTask(const ParallelTask& task, std::shared_ptr<std::atomic<int>> counter, std::shared_ptr<std::promise<void>> promise)
|
||||||
|
{
|
||||||
|
// Increment counter for task group.
|
||||||
|
counter->fetch_add(1, std::memory_order_relaxed);
|
||||||
|
|
||||||
|
// Add task with promise and counter handling.
|
||||||
|
_tasks.push([this, task, counter, promise]() { HandleTask(task, *counter, *promise); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void ParallelTaskManager::HandleTask(const ParallelTask& task, std::atomic<int>& counter, std::promise<void>& promise)
|
||||||
|
{
|
||||||
|
// Execute task.
|
||||||
|
if (task)
|
||||||
|
task();
|
||||||
|
|
||||||
|
// Check for task group completion.
|
||||||
|
if (counter.fetch_sub(1, std::memory_order_acq_rel) == 1)
|
||||||
|
promise.set_value();
|
||||||
|
}
|
||||||
|
}
|
54
TombEngine/Specific/Parallel.h
Normal file
54
TombEngine/Specific/Parallel.h
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace TEN::Utils
|
||||||
|
{
|
||||||
|
using ParallelTask = std::function<void()>;
|
||||||
|
using ParallelTaskGroup = std::vector<ParallelTask>;
|
||||||
|
|
||||||
|
class ParallelTaskManager
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
// Fields
|
||||||
|
|
||||||
|
std::vector<std::thread> _threads = {};
|
||||||
|
std::queue<ParallelTask> _tasks = {};
|
||||||
|
std::mutex _taskMutex = {};
|
||||||
|
std::condition_variable _taskCond = {};
|
||||||
|
bool _deinitialize = false;
|
||||||
|
|
||||||
|
// Constructors, destructors
|
||||||
|
|
||||||
|
ParallelTaskManager();
|
||||||
|
ParallelTaskManager(const ParallelTaskManager& manager) = delete;
|
||||||
|
~ParallelTaskManager();
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Getters
|
||||||
|
|
||||||
|
static ParallelTaskManager& Get();
|
||||||
|
|
||||||
|
unsigned int GetThreadCount() const;
|
||||||
|
unsigned int GetCoreCount() const;
|
||||||
|
|
||||||
|
// Utilities
|
||||||
|
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
|
std::future<void> AddTask(const ParallelTask& task);
|
||||||
|
std::future<void> AddTasks(const ParallelTaskGroup& tasks);
|
||||||
|
std::future<void> AddTasks(int elementCount, const std::function<void(int, int)>& splitTask);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Helpers
|
||||||
|
|
||||||
|
void Worker();
|
||||||
|
void AddTask(const ParallelTask& task, std::shared_ptr<std::atomic<int>> counter, std::shared_ptr<std::promise<void>> promise);
|
||||||
|
void HandleTask(const ParallelTask& task, std::atomic<int>& counter, std::promise<void>& promise);
|
||||||
|
|
||||||
|
// Operators
|
||||||
|
|
||||||
|
ParallelTaskManager& operator =(const ParallelTaskManager& manager) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern ParallelTaskManager& g_Parallel;
|
||||||
|
}
|
|
@ -14,6 +14,7 @@
|
||||||
#include "Sound/sound.h"
|
#include "Sound/sound.h"
|
||||||
#include "Specific/level.h"
|
#include "Specific/level.h"
|
||||||
#include "Specific/configuration.h"
|
#include "Specific/configuration.h"
|
||||||
|
#include "Specific/Parallel.h"
|
||||||
#include "Specific/trutils.h"
|
#include "Specific/trutils.h"
|
||||||
#include "Scripting/Internal/LanguageScript.h"
|
#include "Scripting/Internal/LanguageScript.h"
|
||||||
#include "Scripting/Include/ScriptInterfaceState.h"
|
#include "Scripting/Include/ScriptInterfaceState.h"
|
||||||
|
@ -592,6 +593,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||||
|
|
||||||
DoTheGame = true;
|
DoTheGame = true;
|
||||||
|
|
||||||
|
g_Parallel.Initialize();
|
||||||
ThreadEnded = false;
|
ThreadEnded = false;
|
||||||
ThreadHandle = BeginThread(GameMain, ThreadID);
|
ThreadHandle = BeginThread(GameMain, ThreadID);
|
||||||
|
|
||||||
|
|
|
@ -954,6 +954,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings.
|
||||||
<ClInclude Include="Specific\Input\Input.h" />
|
<ClInclude Include="Specific\Input\Input.h" />
|
||||||
<ClInclude Include="Specific\Input\InputAction.h" />
|
<ClInclude Include="Specific\Input\InputAction.h" />
|
||||||
<ClInclude Include="Specific\LevelCameraInfo.h" />
|
<ClInclude Include="Specific\LevelCameraInfo.h" />
|
||||||
|
<ClInclude Include="Specific\Parallel.h" />
|
||||||
<ClInclude Include="Specific\RGBAColor8Byte.h" />
|
<ClInclude Include="Specific\RGBAColor8Byte.h" />
|
||||||
<ClInclude Include="Specific\clock.h" />
|
<ClInclude Include="Specific\clock.h" />
|
||||||
<ClInclude Include="Specific\configuration.h" />
|
<ClInclude Include="Specific\configuration.h" />
|
||||||
|
@ -1375,6 +1376,7 @@ if not exist "%ScriptsDir%\Strings.lua" xcopy /Y "$(SolutionDir)Scripts\Strings.
|
||||||
<ClCompile Include="Specific\IO\ChunkReader.cpp" />
|
<ClCompile Include="Specific\IO\ChunkReader.cpp" />
|
||||||
<ClCompile Include="Specific\IO\Streams.cpp" />
|
<ClCompile Include="Specific\IO\Streams.cpp" />
|
||||||
<ClCompile Include="Specific\level.cpp" />
|
<ClCompile Include="Specific\level.cpp" />
|
||||||
|
<ClCompile Include="Specific\Parallel.cpp" />
|
||||||
<ClCompile Include="Specific\RGBAColor8Byte.cpp" />
|
<ClCompile Include="Specific\RGBAColor8Byte.cpp" />
|
||||||
<ClCompile Include="Specific\trutils.cpp" />
|
<ClCompile Include="Specific\trutils.cpp" />
|
||||||
<ClCompile Include="Specific\winmain.cpp" />
|
<ClCompile Include="Specific\winmain.cpp" />
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <d3d11.h>
|
#include <d3d11.h>
|
||||||
#include <d3dcompiler.h>
|
#include <d3dcompiler.h>
|
||||||
#include <deque>
|
#include <deque>
|
||||||
|
@ -20,6 +21,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <queue>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
using namespace DirectX;
|
using namespace DirectX;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue