From b9d79a67276882181e143dff944a7c7bd4b1d587 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Tue, 30 Sep 2025 01:02:00 +0200 Subject: [PATCH 1/8] cmake: add OpenMP support --- cmake/DaemonFlags.cmake | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/cmake/DaemonFlags.cmake b/cmake/DaemonFlags.cmake index 24e984ac4d..fc17f4e959 100644 --- a/cmake/DaemonFlags.cmake +++ b/cmake/DaemonFlags.cmake @@ -259,6 +259,10 @@ if (USE_FLOAT_EXCEPTIONS) add_definitions(-DDAEMON_USE_FLOAT_EXCEPTIONS) endif() +if (NOT NACL AND BUILD_CLIENT) + option(USE_OPENMP "Use OpenMP to parallelize some tasks" OFF) +endif() + if (MSVC) set_c_cxx_flag("/MP") @@ -267,6 +271,14 @@ if (MSVC) set_cxx_flag("/std:c++23preview") endif() + if (NOT NACL AND BUILD_CLIENT AND USE_OPENMP) + try_cxx_flag(OPENMP "/openmp") + + if (NOT FLAG_OPENMP) + message(WARNING "Missing OpenMP") + endif() + endif() + if (USE_FAST_MATH) set_c_cxx_flag("/fp:fast") else() @@ -362,6 +374,14 @@ else() endif() endif() + if (NOT NACL AND BUILD_CLIENT AND USE_OPENMP) + try_cxx_flag(FOPENMP "-fopenmp") + + if (NOT FLAG_FOPENMP) + message(WARNING "Missing OpenMP") + endif() + endif() + if (NACL AND USE_NACL_SAIGO AND SAIGO_ARCH STREQUAL "arm") # This should be set for every build type because build type flags # are set after the other custom flags and then have the last word. From 627325e359843d4d36397471dcf7c9470efb22ad Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Tue, 30 Sep 2025 01:14:00 +0200 Subject: [PATCH 2/8] framework: add Omp facilities --- src.cmake | 2 + src/engine/framework/Omp.cpp | 104 +++++++++++++++++++++++++++++++++++ src/engine/framework/Omp.h | 41 ++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 src/engine/framework/Omp.cpp create mode 100644 src/engine/framework/Omp.h diff --git a/src.cmake b/src.cmake index 8d7b3bd4bf..2d36fd5b0a 100644 --- a/src.cmake +++ b/src.cmake @@ -188,6 +188,8 @@ set(ENGINELIST ${ENGINE_DIR}/framework/CvarSystem.h ${ENGINE_DIR}/framework/LogSystem.cpp ${ENGINE_DIR}/framework/LogSystem.h + ${ENGINE_DIR}/framework/Omp.cpp + ${ENGINE_DIR}/framework/Omp.h ${ENGINE_DIR}/framework/Resource.cpp ${ENGINE_DIR}/framework/Resource.h ${ENGINE_DIR}/framework/System.cpp diff --git a/src/engine/framework/Omp.cpp b/src/engine/framework/Omp.cpp new file mode 100644 index 0000000000..b0a3dbead9 --- /dev/null +++ b/src/engine/framework/Omp.cpp @@ -0,0 +1,104 @@ +/* +=========================================================================== +Daemon BSD Source Code +Copyright (c) 2025, Daemon Developers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Daemon developers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +=========================================================================== +*/ + +#include + +#include "CvarSystem.h" +#include "Omp.h" + +#if defined(_OPENMP) +#include "omp.h" +#endif + +#if defined(_OPENMP) +static Cvar::Range> common_ompThreads( + "common.ompThreads", "OpenMP threads", Cvar::NONE, 0, 0, 32 ); +#endif + +namespace Omp { +#if defined(_OPENMP) +static int ompMaxThreads = 1; +#endif + +static int ompThreads = 1; + +static void ReadMaxThreads() +{ +#if defined(_OPENMP) + ompMaxThreads = omp_get_max_threads(); +#endif +} + +void EnlistThreads() +{ +#if defined(_OPENMP) + omp_set_num_threads( ompThreads ); +#endif +} + +void SetupThreads() +{ +#if defined(_OPENMP) + if ( common_ompThreads.Get() ) + { + ompThreads = common_ompThreads.Get(); + return; + } + + if ( ompMaxThreads <= 4 ) + { + ompThreads = ompMaxThreads; + return; + } + + if ( ompMaxThreads <= 16 ) + { + ompThreads = ompMaxThreads - ( ompMaxThreads / 4 ); + return; + } + + ompThreads = 16; +#endif + + EnlistThreads(); +} + +void Init() +{ + ReadMaxThreads(); + SetupThreads(); + EnlistThreads(); +} + +int GetThreads() +{ + return ompThreads; +} +} diff --git a/src/engine/framework/Omp.h b/src/engine/framework/Omp.h new file mode 100644 index 0000000000..72eed78199 --- /dev/null +++ b/src/engine/framework/Omp.h @@ -0,0 +1,41 @@ +/* +=========================================================================== +Daemon BSD Source Code +Copyright (c) 2025, Daemon Developers +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the Daemon developers nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DAEMON DEVELOPERS BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +=========================================================================== +*/ + +#ifndef COMMON_OMP_H_ +#define COMMON_OMP_H_ + +namespace Omp { +void EnlistThreads(); +void SetupThreads(); +void Init(); +int GetThreads(); +}; + +#endif // COMMON_OMP_H_ From 72a92bb35b131e49312815271e7125f222c0431a Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Tue, 30 Sep 2025 01:33:00 +0200 Subject: [PATCH 3/8] engine: initialize Omp --- src/engine/framework/System.cpp | 3 +++ src/engine/qcommon/common.cpp | 3 +++ src/engine/renderer/tr_backend.cpp | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/engine/framework/System.cpp b/src/engine/framework/System.cpp index 2d66f2a791..fb75aa4d01 100644 --- a/src/engine/framework/System.cpp +++ b/src/engine/framework/System.cpp @@ -34,6 +34,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ConsoleHistory.h" #include "CommandSystem.h" #include "LogSystem.h" +#include "Omp.h" #include "System.h" #include "CrashDump.h" #include "CvarSystem.h" @@ -829,6 +830,8 @@ static void SetCvarsWithInitFlag(cmdlineArgs_t& cmdlineArgs) // Initialize the engine static void Init(int argc, char** argv) { + Omp::Init(); + cmdlineArgs_t cmdlineArgs; #ifdef _WIN32 diff --git a/src/engine/qcommon/common.cpp b/src/engine/qcommon/common.cpp index d6f6609598..94b95e324d 100644 --- a/src/engine/qcommon/common.cpp +++ b/src/engine/qcommon/common.cpp @@ -47,6 +47,7 @@ Maryland 20850 USA. #include "framework/CommandSystem.h" #include "framework/CvarSystem.h" #include "framework/LogSystem.h" +#include "framework/Omp.h" #include "framework/System.h" #include "sys/sys_events.h" #include @@ -783,6 +784,8 @@ static Cvar::Cvar showTraceStats("common.showTraceStats", "are physics tra void Com_Frame() { + Omp::SetupThreads(); + int msec, minMsec; static int lastTime = 0; //int key; diff --git a/src/engine/renderer/tr_backend.cpp b/src/engine/renderer/tr_backend.cpp index 43f74375a0..076bea7182 100644 --- a/src/engine/renderer/tr_backend.cpp +++ b/src/engine/renderer/tr_backend.cpp @@ -22,6 +22,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // tr_backend.c +#include "framework/Omp.h" #include "tr_local.h" #include "gl_shader.h" #include "Material.h" @@ -3829,6 +3830,8 @@ RB_RenderThread */ void RB_RenderThread() { + Omp::EnlistThreads(); + const void *data; // wait for either a rendering command or a quit command From 2aada0998946faca9529bbfb8c29f5044d15261f Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Tue, 30 Sep 2025 05:29:00 +0200 Subject: [PATCH 4/8] tr_surface: prepare Tess_SurfaceIQM() for parallelism --- src/engine/renderer/tr_surface.cpp | 85 +++++++++++++++++------------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/src/engine/renderer/tr_surface.cpp b/src/engine/renderer/tr_surface.cpp index d12279d07f..a051b7cc22 100644 --- a/src/engine/renderer/tr_surface.cpp +++ b/src/engine/renderer/tr_surface.cpp @@ -1239,8 +1239,7 @@ void Tess_SurfaceIQM( srfIQModel_t *surf ) { float *modelTangent = model->tangents + 3 * firstVertex; float *modelBitangent = model->bitangents + 3 * firstVertex; float *modelTexcoord = model->texcoords + 2 * firstVertex; - shaderVertex_t *tessVertex = tess.verts + tess.numVertexes; - shaderVertex_t *lastVertex = tessVertex + surf->num_vertexes; + shaderVertex_t *modelTessVertex = tess.verts + tess.numVertexes; // Deform the vertices by the lerped bones. if ( model->num_joints > 0 && model->blendWeights && model->blendIndexes ) @@ -1252,33 +1251,36 @@ void Tess_SurfaceIQM( srfIQModel_t *surf ) { byte *modelBlendIndex = model->blendIndexes + 4 * firstVertex; byte *modelBlendWeight = model->blendWeights + 4 * firstVertex; - for ( ; tessVertex < lastVertex; tessVertex++, - modelPosition += 3, modelNormal += 3, - modelTangent += 3, modelBitangent += 3, - modelTexcoord += 2 ) + for ( size_t i = 0; i < surf->num_vertexes; i++ ) { - vec3_t position = {}; + shaderVertex_t *tessVertex = modelTessVertex + i; + + float *vertexPosition = modelPosition + 3 * i; + float *vertexTexcoord = modelTexcoord + 2 * i; - byte *lastBlendIndex = modelBlendIndex + 4; + byte *blendIndex = modelBlendIndex + 4 * i; + byte *lastBlendIndex = blendIndex + 4; + byte *blendWeight = modelBlendWeight + 4 * i; - for ( ; modelBlendIndex < lastBlendIndex; modelBlendIndex++, - modelBlendWeight++ ) + vec3_t position = {}; + + for ( ; blendIndex < lastBlendIndex; blendIndex++, blendWeight++ ) { - if ( *modelBlendWeight == 0 ) + if ( *blendWeight == 0 ) { continue; } - float weight = *modelBlendWeight * weightFactor; + float weight = *blendWeight * weightFactor; vec3_t tmp; - TransformPoint( &bones[ *modelBlendIndex ], modelPosition, tmp ); + TransformPoint( &bones[ *blendIndex ], vertexPosition, tmp ); VectorMA( position, weight, tmp, position ); } VectorCopy( position, tessVertex->xyz ); - Vector2Copy( modelTexcoord, tessVertex->texCoords ); + Vector2Copy( vertexTexcoord, tessVertex->texCoords ); } } else @@ -1286,36 +1288,42 @@ void Tess_SurfaceIQM( srfIQModel_t *surf ) { byte *modelBlendIndex = model->blendIndexes + 4 * firstVertex; byte *modelBlendWeight = model->blendWeights + 4 * firstVertex; - for ( ; tessVertex < lastVertex; tessVertex++, - modelPosition += 3, modelNormal += 3, - modelTangent += 3, modelBitangent += 3, - modelTexcoord += 2 ) + for ( size_t i = 0; i < surf->num_vertexes; i++ ) { - vec3_t position = {}, tangent = {}, binormal = {}, normal = {}; + shaderVertex_t *tessVertex = modelTessVertex + i; - byte *lastBlendIndex = modelBlendIndex + 4; + float *vertexPosition = modelPosition + 3 * i; + float *vertexNormal = modelNormal + 3 * i; + float *vertexTangent = modelTangent + 3 * i; + float *vertexBitangent = modelBitangent + 3 * i; + float *vertexTexcoord = modelTexcoord + 2 * i; - for ( ; modelBlendIndex < lastBlendIndex; modelBlendIndex++, - modelBlendWeight++ ) + byte *blendIndex = modelBlendIndex + 4 * i; + byte *lastBlendIndex = blendIndex + 4; + byte *blendWeight = modelBlendWeight + 4 * i; + + vec3_t position = {}, tangent = {}, binormal = {}, normal = {}; + + for ( ; blendIndex < lastBlendIndex; blendIndex++, blendWeight++ ) { - if ( *modelBlendWeight == 0 ) + if ( *blendWeight == 0 ) { continue; } - float weight = *modelBlendWeight * weightFactor; + float weight = *blendWeight * weightFactor; vec3_t tmp; - TransformPoint( &bones[ *modelBlendIndex ], modelPosition, tmp ); + TransformPoint( &bones[ *blendIndex ], vertexPosition, tmp ); VectorMA( position, weight, tmp, position ); - TransformNormalVector( &bones[ *modelBlendIndex ], modelNormal, tmp ); + TransformNormalVector( &bones[ *blendIndex ], vertexNormal, tmp ); VectorMA( normal, weight, tmp, normal ); - TransformNormalVector( &bones[ *modelBlendIndex ], modelTangent, tmp ); + TransformNormalVector( &bones[ *blendIndex ], vertexTangent, tmp ); VectorMA( tangent, weight, tmp, tangent ); - TransformNormalVector( &bones[ *modelBlendIndex ], modelBitangent, tmp ); + TransformNormalVector( &bones[ *blendIndex ], vertexBitangent, tmp ); VectorMA( binormal, weight, tmp, binormal ); } @@ -1326,7 +1334,7 @@ void Tess_SurfaceIQM( srfIQModel_t *surf ) { R_TBNtoQtangentsFast( tangent, binormal, normal, tessVertex->qtangents ); - Vector2Copy( modelTexcoord, tessVertex->texCoords ); + Vector2Copy( vertexTexcoord, tessVertex->texCoords ); } } } @@ -1334,16 +1342,21 @@ void Tess_SurfaceIQM( srfIQModel_t *surf ) { { float scale = model->internalScale * backEnd.currentEntity->e.skeleton.scale; - for ( ; tessVertex < lastVertex; tessVertex++, - modelPosition += 3, modelNormal += 3, - modelTangent += 3, modelBitangent += 3, - modelTexcoord += 2 ) + for ( size_t i = 0; i < surf->num_vertexes; i++ ) { - VectorScale( modelPosition, scale, tessVertex->xyz ); + shaderVertex_t *tessVertex = modelTessVertex + i; + + float *vertexPosition = modelPosition + 3 * i; + float *vertexNormal = modelNormal + 3 * i; + float *vertexTangent = modelTangent + 3 * i; + float *vertexBitangent = modelBitangent + 3 * i; + float *vertexTexcoord = modelTexcoord + 2 * i; + + VectorScale( vertexPosition, scale, tessVertex->xyz ); - R_TBNtoQtangentsFast( modelTangent, modelBitangent, modelNormal, tessVertex->qtangents ); + R_TBNtoQtangentsFast( vertexTangent, vertexBitangent, vertexNormal, tessVertex->qtangents ); - Vector2Copy( modelTexcoord, tessVertex->texCoords ); + Vector2Copy( vertexTexcoord, tessVertex->texCoords ); } } From 6152d8e3df1a01790e341d91bc6463adf7e79a08 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Tue, 30 Sep 2025 05:39:00 +0200 Subject: [PATCH 5/8] tr_surface: parallelize Tess_SurfaceIQM() --- src/engine/renderer/tr_surface.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/engine/renderer/tr_surface.cpp b/src/engine/renderer/tr_surface.cpp index a051b7cc22..af94e89cbe 100644 --- a/src/engine/renderer/tr_surface.cpp +++ b/src/engine/renderer/tr_surface.cpp @@ -1251,6 +1251,7 @@ void Tess_SurfaceIQM( srfIQModel_t *surf ) { byte *modelBlendIndex = model->blendIndexes + 4 * firstVertex; byte *modelBlendWeight = model->blendWeights + 4 * firstVertex; + #pragma omp parallel for for ( size_t i = 0; i < surf->num_vertexes; i++ ) { shaderVertex_t *tessVertex = modelTessVertex + i; @@ -1288,6 +1289,7 @@ void Tess_SurfaceIQM( srfIQModel_t *surf ) { byte *modelBlendIndex = model->blendIndexes + 4 * firstVertex; byte *modelBlendWeight = model->blendWeights + 4 * firstVertex; + #pragma omp parallel for for ( size_t i = 0; i < surf->num_vertexes; i++ ) { shaderVertex_t *tessVertex = modelTessVertex + i; @@ -1342,6 +1344,7 @@ void Tess_SurfaceIQM( srfIQModel_t *surf ) { { float scale = model->internalScale * backEnd.currentEntity->e.skeleton.scale; + #pragma omp parallel for for ( size_t i = 0; i < surf->num_vertexes; i++ ) { shaderVertex_t *tessVertex = modelTessVertex + i; From 9a79b0939eee37434075e0ac21676ca92e028157 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 2 Oct 2025 02:49:00 +0200 Subject: [PATCH 6/8] tr_surface: prepare Tess_SurfaceMD5() for parallelism --- src/engine/renderer/tr_surface.cpp | 63 ++++++++++++++++-------------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/src/engine/renderer/tr_surface.cpp b/src/engine/renderer/tr_surface.cpp index af94e89cbe..7a0347c3c3 100644 --- a/src/engine/renderer/tr_surface.cpp +++ b/src/engine/renderer/tr_surface.cpp @@ -1051,66 +1051,69 @@ static void Tess_SurfaceMD5( md5Surface_t *srf ) tessIndex[ 2 ] = tess.numVertexes + surfaceTriangle->indexes[ 2 ]; } - shaderVertex_t *tessVertex = tess.verts + tess.numVertexes; - shaderVertex_t *lastVertex = tessVertex + srf->numVerts; + shaderVertex_t *modelTessVertex = tess.verts + tess.numVertexes; // Deform the vertices by the lerped bones. if ( tess.skipTangents ) { - for ( ; tessVertex < lastVertex; tessVertex++, - surfaceVertex++ ) + for ( size_t i = 0; i < srf->numVerts; i++ ) { - vec3_t position = {}; + shaderVertex_t *tessVertex = modelTessVertex + i; + md5Vertex_t *vertex = surfaceVertex + i; - float *boneWeight = surfaceVertex->boneWeights; - float *lastWeight = boneWeight + surfaceVertex->numWeights; - uint32_t *boneIndex = surfaceVertex->boneIndexes; - vec4_t *surfacePosition = &surfaceVertex->position; + vec4_t *vertexPosition = &vertex->position; + + float *boneWeight = vertex->boneWeights; + float *lastWeight = boneWeight + vertex->numWeights; + uint32_t *boneIndex = vertex->boneIndexes; + + vec3_t position = {}; - for ( ; boneWeight < lastWeight; boneWeight++, - boneIndex++ ) + for ( ; boneWeight < lastWeight; boneWeight++, boneIndex++ ) { vec3_t tmp; - TransformPoint( &bones[ *boneIndex ], *surfacePosition, tmp ); + TransformPoint( &bones[ *boneIndex ], *vertexPosition, tmp ); VectorMA( position, *boneWeight, tmp, position ); } VectorCopy( position, tessVertex->xyz ); - Vector2Copy( surfaceVertex->texCoords, tessVertex->texCoords ); + Vector2Copy( vertex->texCoords, tessVertex->texCoords ); } } else { - for ( ; tessVertex < lastVertex; tessVertex++, - surfaceVertex++ ) + for ( size_t i = 0; i < srf->numVerts; i++ ) { - vec3_t tangent = {}, binormal = {}, normal = {}, position = {}; + shaderVertex_t *tessVertex = modelTessVertex + i; + md5Vertex_t *vertex = surfaceVertex + i; + + vec4_t *vertexPosition = &vertex->position; + vec4_t *vertexNormal = &vertex->normal; + vec4_t *vertexTangent = &vertex->tangent; + vec4_t *vertexBinormal = &vertex->binormal; - float *boneWeight = surfaceVertex->boneWeights; - float *lastWeight = boneWeight + surfaceVertex->numWeights; - uint32_t *boneIndex = surfaceVertex->boneIndexes; - vec4_t *surfacePosition = &surfaceVertex->position; - vec4_t *surfaceNormal = &surfaceVertex->normal; - vec4_t *surfaceTangent = &surfaceVertex->tangent; - vec4_t *surfaceBinormal = &surfaceVertex->binormal; + float *boneWeight = vertex->boneWeights; + float *lastWeight = boneWeight + vertex->numWeights; + uint32_t *boneIndex = vertex->boneIndexes; + + vec3_t tangent = {}, binormal = {}, normal = {}, position = {}; - for ( ; boneWeight < lastWeight; boneWeight++, - boneIndex++ ) + for ( ; boneWeight < lastWeight; boneWeight++, boneIndex++ ) { vec3_t tmp; - TransformPoint( &bones[ *boneIndex ], *surfacePosition, tmp ); + TransformPoint( &bones[ *boneIndex ], *vertexPosition, tmp ); VectorMA( position, *boneWeight, tmp, position ); - TransformNormalVector( &bones[ *boneIndex ], *surfaceNormal, tmp ); + TransformNormalVector( &bones[ *boneIndex ], *vertexNormal, tmp ); VectorMA( normal, *boneWeight, tmp, normal ); - TransformNormalVector( &bones[ *boneIndex ], *surfaceTangent, tmp ); + TransformNormalVector( &bones[ *boneIndex ], *vertexTangent, tmp ); VectorMA( tangent, *boneWeight, tmp, tangent ); - TransformNormalVector( &bones[ *boneIndex ], *surfaceBinormal, tmp ); + TransformNormalVector( &bones[ *boneIndex ], *vertexBinormal, tmp ); VectorMA( binormal, *boneWeight, tmp, binormal ); } @@ -1121,7 +1124,7 @@ static void Tess_SurfaceMD5( md5Surface_t *srf ) R_TBNtoQtangentsFast( tangent, binormal, normal, tessVertex->qtangents ); - Vector2Copy( surfaceVertex->texCoords, tessVertex->texCoords ); + Vector2Copy( vertex->texCoords, tessVertex->texCoords ); } } From ec7395e53d423389eabeedc260f78c45e7a32d42 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Thu, 2 Oct 2025 02:59:00 +0200 Subject: [PATCH 7/8] tr_surface: parallelize Tess_SurfaceMD5() --- src/engine/renderer/tr_surface.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/engine/renderer/tr_surface.cpp b/src/engine/renderer/tr_surface.cpp index 7a0347c3c3..120543dc52 100644 --- a/src/engine/renderer/tr_surface.cpp +++ b/src/engine/renderer/tr_surface.cpp @@ -1056,6 +1056,7 @@ static void Tess_SurfaceMD5( md5Surface_t *srf ) // Deform the vertices by the lerped bones. if ( tess.skipTangents ) { + #pragma omp parallel for for ( size_t i = 0; i < srf->numVerts; i++ ) { shaderVertex_t *tessVertex = modelTessVertex + i; @@ -1084,6 +1085,7 @@ static void Tess_SurfaceMD5( md5Surface_t *srf ) } else { + #pragma omp parallel for for ( size_t i = 0; i < srf->numVerts; i++ ) { shaderVertex_t *tessVertex = modelTessVertex + i; From 8355d4778a43f394567d0410a3b2035510d429d6 Mon Sep 17 00:00:00 2001 From: Thomas Debesse Date: Fri, 3 Oct 2025 03:15:44 +0200 Subject: [PATCH 8/8] tr_init: report OpenMP support in /gfxinfo --- src/engine/renderer/tr_init.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/engine/renderer/tr_init.cpp b/src/engine/renderer/tr_init.cpp index 7016b35d9a..494a4c6634 100644 --- a/src/engine/renderer/tr_init.cpp +++ b/src/engine/renderer/tr_init.cpp @@ -23,6 +23,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // tr_init.c -- functions that are not called every frame #include "tr_local.h" #include "framework/CvarSystem.h" +#include "framework/Omp.h" #include "DetectGLVendors.h" #include "Material.h" #include "GeometryCache.h" @@ -1055,6 +1056,21 @@ ScreenshotCmd screenshotPNGRegistration("screenshotPNG", ssFormat_t::SSF_PNG, "p Log::Notice("Using dual processor acceleration." ); } +#if defined(_OPENMP) + int ompThreads = Omp::GetThreads(); + + if ( ompThreads == 1 ) + { + Log::Notice("%sNot using OpenMP parallelism: only one thread.", Color::ToString( Color::Red ) ); + } + else + { + Log::Notice("%sUsing OpenMP parallelism with %d threads.", Color::ToString( Color::Green ), ompThreads ); + } +#else + Log::Notice("%sNot using OpenMP parallelism: unavailable.", Color::ToString( Color::Red ) ); +#endif + if ( r_finish->integer ) { Log::Notice("Forcing glFinish." );