diff --git a/neo/doom3/game/gamesys/GameTypeInfo.h b/neo/doom3/game/gamesys/GameTypeInfo.h index 3713c514..cba585e5 100644 --- a/neo/doom3/game/gamesys/GameTypeInfo.h +++ b/neo/doom3/game/gamesys/GameTypeInfo.h @@ -4303,6 +4303,8 @@ static classVariableInfo_t srfTriangles_t_typeInfo[] = { { "vertCache_s *", "ambientCache", (intptr_t)(&((srfTriangles_t *)0)->ambientCache), sizeof( ((srfTriangles_t *)0)->ambientCache ) }, { "vertCache_s *", "lightingCache", (intptr_t)(&((srfTriangles_t *)0)->lightingCache), sizeof( ((srfTriangles_t *)0)->lightingCache ) }, { "vertCache_s *", "shadowCache", (intptr_t)(&((srfTriangles_t *)0)->shadowCache), sizeof( ((srfTriangles_t *)0)->shadowCache ) }, + { "unsigned int", "ambientVbo", (intptr_t)(&((srfTriangles_t *)0)->ambientVbo), sizeof( ((srfTriangles_t *)0)->ambientVbo ) }, + { "unsigned int", "indexVbo", (intptr_t)(&((srfTriangles_t *)0)->indexVbo), sizeof( ((srfTriangles_t *)0)->indexVbo ) }, { "bool", "isSkeletal", (intptr_t)(&((srfTriangles_t *)0)->isSkeletal), sizeof( ((srfTriangles_t *)0)->isSkeletal ) }, { "int", "numAllocedVerts", (intptr_t)(&((srfTriangles_t *)0)->numAllocedVerts), sizeof( ((srfTriangles_t *)0)->numAllocedVerts ) }, { "int", "numAllocedIndices", (intptr_t)(&((srfTriangles_t *)0)->numAllocedIndices), sizeof( ((srfTriangles_t *)0)->numAllocedIndices ) }, diff --git a/neo/engine/doomdll.vcxproj b/neo/engine/doomdll.vcxproj index 2ddbfab8..ad5d85c6 100644 --- a/neo/engine/doomdll.vcxproj +++ b/neo/engine/doomdll.vcxproj @@ -1606,6 +1606,7 @@ + @@ -3102,4 +3103,4 @@ - \ No newline at end of file + diff --git a/neo/engine/doomdll.vcxproj.filters b/neo/engine/doomdll.vcxproj.filters index 385d7b76..fa236aac 100644 --- a/neo/engine/doomdll.vcxproj.filters +++ b/neo/engine/doomdll.vcxproj.filters @@ -782,6 +782,9 @@ Renderer + + Renderer + Renderer @@ -2420,4 +2423,4 @@ Sys\RC\res - \ No newline at end of file + diff --git a/neo/engine/doomdll.vcxproj.user b/neo/engine/doomdll.vcxproj.user index 20aa55cb..08538da5 100644 --- a/neo/engine/doomdll.vcxproj.user +++ b/neo/engine/doomdll.vcxproj.user @@ -17,7 +17,7 @@ WindowsLocalDebugger +set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 +set fs_game e3 D:\projects\Doom3 - +set r_fullscreen 0 +set r_mode 9|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 |+set r_fullscreen 1 +set r_mode 9 +set sv_pure 0 +set fs_game e3|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 +set fs_game e3| + +set r_fullscreen 0 +set r_mode 9|+set r_fullscreen 1 +set r_mode 9 +set sv_pure 0 +set fs_game e3|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0|+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 |+set r_fullscreen 0 +set r_mode 9 +set sv_pure 0 +set fs_game e3| D:\projects\Doom3\Quake4.exe diff --git a/neo/engine/framework/Common.cpp b/neo/engine/framework/Common.cpp index fdf806c3..c9cb1bdd 100644 --- a/neo/engine/framework/Common.cpp +++ b/neo/engine/framework/Common.cpp @@ -123,6 +123,9 @@ int time_gameFrame; int time_gameDraw; int time_frontend; // renderSystem frontend time int time_backend; // renderSystem backend time +float com_lastGameFrameMsec; +float com_lastRenderFrameMsec; +float com_pathTracingGpuMsec; #ifdef QUAKE4 int time_waiting; // wait time, surfaced by Quake 4's timing/debug HUD path @@ -3211,6 +3214,9 @@ void idCommonLocal::Frame(void) { session->UpdateScreen(false); } + com_lastGameFrameMsec = (float)time_gameFrame; + com_lastRenderFrameMsec = (float)(time_gameDraw + time_frontend + time_backend); + // report timing information if (com_speeds.GetBool()) { static int lastTime; @@ -3221,6 +3227,12 @@ void idCommonLocal::Frame(void) { time_gameFrame = 0; time_gameDraw = 0; } + else { + time_gameFrame = 0; + time_gameDraw = 0; + time_frontend = 0; + time_backend = 0; + } com_frameNumber++; @@ -4051,4 +4063,4 @@ void idCommonLocal::MaterialKeyForBinding(const char* binding, char* materialNam strcpy(keyText, displayName.c_str()); *wideKey = true; } -#endif \ No newline at end of file +#endif diff --git a/neo/engine/framework/Common.h b/neo/engine/framework/Common.h index b38ed912..d43bca38 100644 --- a/neo/engine/framework/Common.h +++ b/neo/engine/framework/Common.h @@ -177,6 +177,9 @@ extern int time_gameFrame; // game logic time extern int time_gameDraw; // game present time extern int time_frontend; // renderer frontend time extern int time_backend; // renderer backend time +extern float com_lastGameFrameMsec; // last displayed game CPU time +extern float com_lastRenderFrameMsec;// last displayed render CPU time +extern float com_pathTracingGpuMsec; // last completed path tracing GPU time #ifdef QUAKE4 extern int time_waiting; // time spent waiting #endif diff --git a/neo/engine/framework/Console.cpp b/neo/engine/framework/Console.cpp index 2747d457..c20805a7 100644 --- a/neo/engine/framework/Console.cpp +++ b/neo/engine/framework/Console.cpp @@ -212,6 +212,12 @@ float SCR_DrawFPS( float y ) { w = strlen( s ) * BIGCHAR_WIDTH; renderSystem->DrawBigStringExt( (SCREEN_WIDTH - 5.0f) - w, idMath::FtoiFast(y) + 2, s, colorWhite, true, localConsole.charSetShader); + + y += BIGCHAR_HEIGHT + 4; + s = va( "game cpu:%4.1fms render cpu:%4.1fms pt gpu:%4.1fms", com_lastGameFrameMsec, com_lastRenderFrameMsec, com_pathTracingGpuMsec ); + w = strlen( s ) * SMALLCHAR_WIDTH; + renderSystem->DrawSmallStringExt( (SCREEN_WIDTH - 5.0f) - w, idMath::FtoiFast(y) + 2, s, colorWhite, true, localConsole.charSetShader); + return y + SMALLCHAR_HEIGHT + 4; } return y + BIGCHAR_HEIGHT + 4; diff --git a/neo/engine/framework/Session.cpp b/neo/engine/framework/Session.cpp index 26f31a9e..6d98e2a7 100644 --- a/neo/engine/framework/Session.cpp +++ b/neo/engine/framework/Session.cpp @@ -2686,7 +2686,7 @@ void idSessionLocal::UpdateScreen( bool outOfSequence ) { // draw everything Draw(); - if ( com_speeds.GetBool() ) { + if ( com_speeds.GetBool() || com_showFPS.GetBool() ) { renderSystem->EndFrame( &time_frontend, &time_backend ); } else { renderSystem->EndFrame( NULL, NULL ); diff --git a/neo/engine/models/Model.cpp b/neo/engine/models/Model.cpp index d5b5e0f6..6ec24cad 100644 --- a/neo/engine/models/Model.cpp +++ b/neo/engine/models/Model.cpp @@ -623,6 +623,8 @@ void idRenderModelStatic::UpdateDXR(uint32_t& dxrBottomAcel, int onlySurface) desc.vertexCount = (uint32_t)numDXRVerts; desc.indices = indices.data(); desc.indexCount = (uint32_t)numDXRIndexes; + desc.allowUpdate = 1; + desc.opaque = 1; if (!dxrBottomAcel) { @@ -2223,6 +2225,14 @@ void idRenderModelStatic::FreeVertexCache( void ) { vertexCache.Free( tri->ambientCache ); tri->ambientCache = NULL; } + if ( tri->ambientVbo ) { + glDeleteBuffersARB( 1, &tri->ambientVbo ); + tri->ambientVbo = 0; + } + if ( tri->indexVbo ) { + glDeleteBuffersARB( 1, &tri->indexVbo ); + tri->indexVbo = 0; + } // static shadows may be present if ( tri->shadowCache ) { vertexCache.Free( tri->shadowCache ); diff --git a/neo/engine/models/Model.h b/neo/engine/models/Model.h index ed6e8db4..5dc94aa8 100644 --- a/neo/engine/models/Model.h +++ b/neo/engine/models/Model.h @@ -138,6 +138,9 @@ typedef struct srfTriangles_s { struct vertCache_s * lightingCache; // lightingCache_t struct vertCache_s * shadowCache; // shadowCache_t + unsigned int ambientVbo; // model-owned idDrawVert GL buffer + unsigned int indexVbo; // model-owned index GL buffer + bool isSkeletal; int numAllocedVerts; int numAllocedIndices; diff --git a/neo/engine/opengl/gl_d3d12raylight.cpp b/neo/engine/opengl/gl_d3d12raylight.cpp index 96f49eef..5b629e1e 100644 --- a/neo/engine/opengl/gl_d3d12raylight.cpp +++ b/neo/engine/opengl/gl_d3d12raylight.cpp @@ -49,6 +49,8 @@ If you have questions concerning this license or the applicable additional terms using Microsoft::WRL::ComPtr; +extern float com_pathTracingGpuMsec; + // ============================================================ // Logging / checks // ============================================================ @@ -407,6 +409,10 @@ struct glRaytracingCmdContext_t ComPtr fence; HANDLE fenceEvent; UINT64 nextFenceValue; + ComPtr pathTracingTimestampHeap; + glRaytracingBuffer_t pathTracingTimestampReadback; + UINT64 pathTracingTimestampFrequency; + UINT64 pathTracingTimestampFenceRing[GL_RAYTRACING_CMD_RING_SIZE]; bool initialized; glRaytracingCmdContext_t() @@ -425,9 +431,11 @@ struct glRaytracingCmdContext_t cmdFenceValueRing[i] = 0; blasFenceValueRing[i] = 0; tlasFenceValueRing[i] = 0; + pathTracingTimestampFenceRing[i] = 0; } fenceEvent = nullptr; nextFenceValue = 0; + pathTracingTimestampFrequency = 0; initialized = false; } }; @@ -463,6 +471,66 @@ static void glRaytracingWaitIdle(void) glRaytracingWaitFenceValue(value); } +static void glRaytracingPollPathTracingTimestamp(UINT slot) +{ + if (!g_glRaytracingCmd.pathTracingTimestampReadback.resource || + !g_glRaytracingCmd.pathTracingTimestampFrequency || + slot >= GL_RAYTRACING_CMD_RING_SIZE) + { + return; + } + + const UINT64 fenceValue = g_glRaytracingCmd.pathTracingTimestampFenceRing[slot]; + if (!fenceValue || !g_glRaytracingCmd.fence || g_glRaytracingCmd.fence->GetCompletedValue() < fenceValue) + return; + + const UINT64 offset = sizeof(UINT64) * 2ull * slot; + D3D12_RANGE readRange = { offset, offset + sizeof(UINT64) * 2ull }; + UINT64* timestamps = nullptr; + if (SUCCEEDED(g_glRaytracingCmd.pathTracingTimestampReadback.resource->Map(0, &readRange, reinterpret_cast(×tamps))) && timestamps != nullptr) + { + const UINT64 begin = timestamps[slot * 2 + 0]; + const UINT64 end = timestamps[slot * 2 + 1]; + if (end >= begin) + { + com_pathTracingGpuMsec = (float)((double)(end - begin) * 1000.0 / (double)g_glRaytracingCmd.pathTracingTimestampFrequency); + } + + D3D12_RANGE writeRange = { 0, 0 }; + g_glRaytracingCmd.pathTracingTimestampReadback.resource->Unmap(0, &writeRange); + } + g_glRaytracingCmd.pathTracingTimestampFenceRing[slot] = 0; +} + +static int glRaytracingCreatePathTracingTimestamps(void) +{ + D3D12_QUERY_HEAP_DESC qh = {}; + qh.Type = D3D12_QUERY_HEAP_TYPE_TIMESTAMP; + qh.Count = GL_RAYTRACING_CMD_RING_SIZE * 2; + if (FAILED(g_glRaytracingCmd.device->CreateQueryHeap(&qh, IID_PPV_ARGS(&g_glRaytracingCmd.pathTracingTimestampHeap)))) + return 0; + + g_glRaytracingCmd.pathTracingTimestampReadback = glRaytracingCreateBuffer( + g_glRaytracingCmd.device.Get(), + sizeof(UINT64) * GL_RAYTRACING_CMD_RING_SIZE * 2ull, + D3D12_HEAP_TYPE_READBACK, + D3D12_RESOURCE_STATE_COPY_DEST, + D3D12_RESOURCE_FLAG_NONE); + if (!g_glRaytracingCmd.pathTracingTimestampReadback.resource) + return 0; + + g_glRaytracingCmd.pathTracingTimestampFrequency = 0; + if (FAILED(g_glRaytracingCmd.queue->GetTimestampFrequency(&g_glRaytracingCmd.pathTracingTimestampFrequency)) || + g_glRaytracingCmd.pathTracingTimestampFrequency == 0) + { + g_glRaytracingCmd.pathTracingTimestampHeap.Reset(); + g_glRaytracingCmd.pathTracingTimestampReadback = glRaytracingBuffer_t(); + return 0; + } + + return 1; +} + static int glRaytracingCreateDirectCommandListPair( ID3D12Device5* device, ComPtr& allocator, @@ -562,6 +630,11 @@ static int glRaytracingInitCmdContext(void) return 0; } + if (!glRaytracingCreatePathTracingTimestamps()) + { + glRaytracingLog("path tracing timestamp queries unavailable"); + } + g_glRaytracingCmd.initialized = true; return 1; } @@ -586,6 +659,7 @@ static int glRaytracingBeginCmd(void) { const UINT slot = (g_glRaytracingCmd.cmdRingIndex + 1u) % GL_RAYTRACING_CMD_RING_SIZE; glRaytracingWaitFenceValue(g_glRaytracingCmd.cmdFenceValueRing[slot]); + glRaytracingPollPathTracingTimestamp(slot); g_glRaytracingCmd.cmdRingIndex = slot; g_glRaytracingCmd.cmdCurrentSlot = slot; @@ -1965,12 +2039,52 @@ int glRaytracingUpdateMesh(glRaytracingMeshHandle_t meshHandle, const glRaytraci if (!desc->vertices || !desc->indices || desc->vertexCount == 0 || desc->indexCount == 0) return 0; + const UINT64 vbBytes = UINT64(desc->vertexCount) * sizeof(glRaytracingVertex_t); + const UINT64 ibBytes = UINT64(desc->indexCount) * sizeof(uint32_t); + const bool sameVertexData = + mesh->verticesCpu.size() == desc->vertexCount && + memcmp(mesh->verticesCpu.data(), desc->vertices, (size_t)vbBytes) == 0; + const bool sameIndexData = + mesh->indicesCpu.size() == desc->indexCount && + memcmp(mesh->indicesCpu.data(), desc->indices, (size_t)ibBytes) == 0; + const bool sameDesc = + mesh->descCpu.vertexCount == desc->vertexCount && + mesh->descCpu.indexCount == desc->indexCount && + mesh->descCpu.allowUpdate == desc->allowUpdate && + mesh->descCpu.opaque == desc->opaque; + + if (sameDesc && sameVertexData && sameIndexData) + return 1; + + const bool canUpdateExistingBuffers = + desc->allowUpdate != 0 && + mesh->descCpu.allowUpdate != 0 && + mesh->descCpu.opaque == desc->opaque && + mesh->blasBuilt != 0 && + mesh->vertexBuffer.resource && + mesh->indexBuffer.resource && + mesh->vertexBuffer.size >= vbBytes && + mesh->indexBuffer.size >= ibBytes && + mesh->verticesCpu.size() == desc->vertexCount && + mesh->indicesCpu.size() == desc->indexCount; + mesh->descCpu = *desc; mesh->verticesCpu.assign(desc->vertices, desc->vertices + desc->vertexCount); mesh->indicesCpu.assign(desc->indices, desc->indices + desc->indexCount); mesh->descCpu.vertices = nullptr; mesh->descCpu.indices = nullptr; + if (canUpdateExistingBuffers) + { + glRaytracingMapCopy(mesh->vertexBuffer.resource.Get(), mesh->verticesCpu.data(), (size_t)vbBytes); + if (!sameIndexData) + glRaytracingMapCopy(mesh->indexBuffer.resource.Get(), mesh->indicesCpu.data(), (size_t)ibBytes); + + mesh->dirty = 1; + glRaytracingInvalidateInstancesForMesh(meshHandle, 0); + return 1; + } + // Updating a mesh destroys/replaces resources that an already submitted frame // may still reference. Wait only for this destructive path; steady-state // rendering remains asynchronous. @@ -2132,6 +2246,9 @@ int glRaytracingUpdateInstanceInScene(glRaytracingSceneHandle_t worldHandle, glR ? 0u : glRaytracingNormalizeVisibleInstanceMask(newDesc.mask); + if (memcmp(&inst->descCpu, &newDesc, sizeof(newDesc)) == 0) + return 1; + inst->descCpu = newDesc; inst->dirty = 1; @@ -6440,6 +6557,18 @@ static bool glRaytracingLightingExecuteInternal( g_glRaytracingCmd.cmdList->SetComputeRootConstantBufferView(2, g_glRaytracingLighting.constantBuffer.gpuVA); g_glRaytracingCmd.cmdList->SetPipelineState1(g_glRaytracingLighting.rtStateObject.Get()); + const UINT timestampBase = g_glRaytracingCmd.cmdCurrentSlot * 2u; + const bool writeTimestamp = + g_glRaytracingCmd.pathTracingTimestampHeap.Get() != nullptr && + g_glRaytracingCmd.pathTracingTimestampReadback.resource.Get() != nullptr; + if (writeTimestamp) + { + g_glRaytracingCmd.cmdList->EndQuery( + g_glRaytracingCmd.pathTracingTimestampHeap.Get(), + D3D12_QUERY_TYPE_TIMESTAMP, + timestampBase + 0u); + } + const UINT shaderRecordSize = (UINT)glRaytracingAlignUp( D3D12_SHADER_IDENTIFIER_SIZE_IN_BYTES, D3D12_RAYTRACING_SHADER_RECORD_BYTE_ALIGNMENT); @@ -6586,9 +6715,27 @@ static bool glRaytracingLightingExecuteInternal( D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE); + if (writeTimestamp) + { + g_glRaytracingCmd.cmdList->EndQuery( + g_glRaytracingCmd.pathTracingTimestampHeap.Get(), + D3D12_QUERY_TYPE_TIMESTAMP, + timestampBase + 1u); + g_glRaytracingCmd.cmdList->ResolveQueryData( + g_glRaytracingCmd.pathTracingTimestampHeap.Get(), + D3D12_QUERY_TYPE_TIMESTAMP, + timestampBase, + 2, + g_glRaytracingCmd.pathTracingTimestampReadback.resource.Get(), + sizeof(UINT64) * timestampBase); + } + if (!glRaytracingEndCmd()) return false; + if (writeTimestamp) + g_glRaytracingCmd.pathTracingTimestampFenceRing[g_glRaytracingCmd.cmdCurrentSlot] = g_glRaytracingCmd.cmdLastFenceValue; + if (useInternalDenoiser) g_glRaytracingLighting.currentHistoryIndex = historyWriteIndex; diff --git a/neo/engine/opengl/gl_d3d12shim.cpp b/neo/engine/opengl/gl_d3d12shim.cpp index 449b805c..e97d9c46 100644 --- a/neo/engine/opengl/gl_d3d12shim.cpp +++ b/neo/engine/opengl/gl_d3d12shim.cpp @@ -710,6 +710,8 @@ struct DrawConstants float texEnvColor1[4]; float cameraPomPad[4]; float neuralPomPad[4]; + float currentColor[4]; + float vertexColorPad[4]; // Appended for translated ARBvp/ARBfp programs. Fixed-function HLSL ignores // these fields; ARB-generated HLSL uses them as program.env/local storage. @@ -723,12 +725,27 @@ struct GLBufferObject GLuint id = 0; GLenum target = 0; GLbitfield storageFlags = 0; + GLenum usage = GL_STATIC_DRAW_ARB; std::vector data; + uint32_t revision = 1; bool mapped = false; GLintptr mappedOffset = 0; GLsizeiptr mappedLength = 0; GLbitfield mappedAccess = 0; + + ComPtr resource; + uint8_t* mappedGpu = nullptr; + D3D12_GPU_VIRTUAL_ADDRESS gpuAddress = 0; + size_t gpuBytes = 0; + + ComPtr packedVertexResource; + uint8_t* packedVertexMapped = nullptr; + D3D12_GPU_VIRTUAL_ADDRESS packedVertexGpuAddress = 0; + size_t packedVertexBytes = 0; + size_t packedVertexCount = 0; + uint32_t packedVertexRevision = 0; + uint64_t packedVertexLayoutHash = 0; }; const char* vendor = "Justin Marshall"; @@ -847,6 +864,8 @@ struct BatchKey float fogStart = 0.0f; float fogEnd = 1.0f; float fogColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + float currentColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + float useVertexColor = 0.0f; GLenum blendSrc = GL_ONE; GLenum blendDst = GL_ZERO; @@ -932,6 +951,8 @@ static bool BatchKeyEquals(const BatchKey& a, const BatchKey& b) a.fogStart == b.fogStart && a.fogEnd == b.fogEnd && memcmp(a.fogColor, b.fogColor, sizeof(a.fogColor)) == 0 && + memcmp(a.currentColor, b.currentColor, sizeof(a.currentColor)) == 0 && + a.useVertexColor == b.useVertexColor && a.colorWriteMask == b.colorWriteMask && a.cullFaceEnabled == b.cullFaceEnabled && a.cullMode == b.cullMode && @@ -995,6 +1016,13 @@ struct QueuedBatch size_t firstVertex; size_t vertexCount; + + bool gpuIndexed = false; + D3D12_VERTEX_BUFFER_VIEW gpuVbv{}; + D3D12_INDEX_BUFFER_VIEW gpuIbv{}; + UINT gpuIndexCount = 0; + ComPtr gpuVertexResource; + ComPtr gpuIndexResource; }; struct QD3D12Window @@ -1195,6 +1223,22 @@ struct ImmediateVertexBuffer count = 0; } + GLVertex* ResizeForWrite(size_t newCount) + { + if (newCount > storage.size()) + { + size_t newSize = storage.empty() ? 1024 : storage.size(); + while (newSize < newCount) + newSize *= 2; + + storage.resize(newSize); + ptr = storage.data(); + } + + count = newCount; + return ptr; + } + GLVertex& Push() { if (count >= storage.size()) @@ -1310,7 +1354,7 @@ struct GLState bool motionHistoryReset = true; QD3D12UpscalerBackend upscalerBackend = QD3D12_UPSCALER_DLSS; - QD3D12UpscalerQuality upscalerQuality = QD3D12_QUALITY_PERFORMANCE; + QD3D12UpscalerQuality upscalerQuality = QD3D12_QUALITY_DLAA; bool enableInternalTAA = true; bool enableRayAIDenoise = false; bool enableDLSSRayReconstruction = true; @@ -1331,6 +1375,8 @@ struct GLState GLenum type = GL_FLOAT; GLsizei stride = 0; const uint8_t* ptr = nullptr; + GLuint buffer = 0; + size_t offset = 0; bool enabled = false; }; @@ -1404,6 +1450,7 @@ struct GLState GLenum blendSrc = GL_SRC_ALPHA; GLenum blendDst = GL_ONE_MINUS_SRC_ALPHA; GLenum alphaFunc = GL_GREATER; + float alphaFuncMapped = 4.0f; float alphaRef = 0.666f; GLenum cullMode = GL_BACK; GLenum frontFace = GL_CCW; @@ -1553,11 +1600,19 @@ struct QD3D12AutoCameraHistory { bool haveLastCamera = false; bool haveFramePrevious = false; + bool haveCachedUpdate = false; uint64_t frameSerial = UINT64_MAX; + uint64_t cachedFrameSerial = UINT64_MAX; + bool cachedMotionHistoryReset = false; + UINT cachedRenderWidth = 0; + UINT cachedRenderHeight = 0; float lastViewToClip[16] = {}; float lastWorldToView[16] = {}; float framePreviousViewToClip[16] = {}; float framePreviousWorldToView[16] = {}; + float cachedProjection[16] = {}; + float cachedModelView[16] = {}; + float cachedModelToWorld[16] = {}; }; static QD3D12AutoCameraHistory g_qd3d12AutoCamera; @@ -1776,6 +1831,21 @@ static bool QD3D12_UpdateCameraInfoFromCurrentMatrices() const float* glProjection = g_gl.projStack.back().m; const float* modelView = g_gl.modelStack.back().m; const float* modelToWorld = g_gl.modelMatrix.m; + const UINT renderWidth = g_currentWindow ? g_currentWindow->renderWidth : 0; + const UINT renderHeight = g_currentWindow ? g_currentWindow->renderHeight : 0; + + if (g_qd3d12AutoCamera.haveCachedUpdate && + g_qd3d12AutoCamera.cachedFrameSerial == g_gl.frameSerial && + g_qd3d12AutoCamera.cachedMotionHistoryReset == g_gl.motionHistoryReset && + g_qd3d12AutoCamera.cachedRenderWidth == renderWidth && + g_qd3d12AutoCamera.cachedRenderHeight == renderHeight && + g_gl.cameraState.valid && + memcmp(g_qd3d12AutoCamera.cachedProjection, glProjection, sizeof(g_qd3d12AutoCamera.cachedProjection)) == 0 && + memcmp(g_qd3d12AutoCamera.cachedModelView, modelView, sizeof(g_qd3d12AutoCamera.cachedModelView)) == 0 && + memcmp(g_qd3d12AutoCamera.cachedModelToWorld, modelToWorld, sizeof(g_qd3d12AutoCamera.cachedModelToWorld)) == 0) + { + return true; + } if (!QD3D12_IsPerspectiveProjectionCM(glProjection) || !QD3D12_MatrixFinite(modelView) || @@ -1893,6 +1963,14 @@ static bool QD3D12_UpdateCameraInfoFromCurrentMatrices() QD3D12_MatrixCopy(g_qd3d12AutoCamera.lastViewToClip, viewToClip); QD3D12_MatrixCopy(g_qd3d12AutoCamera.lastWorldToView, worldToView); g_qd3d12AutoCamera.haveLastCamera = true; + g_qd3d12AutoCamera.haveCachedUpdate = true; + g_qd3d12AutoCamera.cachedFrameSerial = g_gl.frameSerial; + g_qd3d12AutoCamera.cachedMotionHistoryReset = g_gl.motionHistoryReset; + g_qd3d12AutoCamera.cachedRenderWidth = renderWidth; + g_qd3d12AutoCamera.cachedRenderHeight = renderHeight; + QD3D12_MatrixCopy(g_qd3d12AutoCamera.cachedProjection, glProjection); + QD3D12_MatrixCopy(g_qd3d12AutoCamera.cachedModelView, modelView); + QD3D12_MatrixCopy(g_qd3d12AutoCamera.cachedModelToWorld, modelToWorld); return true; } @@ -2205,6 +2283,101 @@ static GLBufferObject* QD3D12_GetBuffer(GLuint id) return &it->second; } +static void QD3D12_ResetPackedVertexBuffer(GLBufferObject& bo) +{ + if (bo.packedVertexResource && bo.packedVertexMapped) + bo.packedVertexResource->Unmap(0, nullptr); + + bo.packedVertexResource.Reset(); + bo.packedVertexMapped = nullptr; + bo.packedVertexGpuAddress = 0; + bo.packedVertexBytes = 0; + bo.packedVertexCount = 0; + bo.packedVertexRevision = 0; + bo.packedVertexLayoutHash = 0; +} + +static void QD3D12_ResetBufferResource(GLBufferObject& bo) +{ + if (bo.resource && bo.mappedGpu) + bo.resource->Unmap(0, nullptr); + + bo.resource.Reset(); + bo.mappedGpu = nullptr; + bo.gpuAddress = 0; + bo.gpuBytes = 0; + QD3D12_ResetPackedVertexBuffer(bo); +} + +static bool QD3D12_CreateUploadBufferResource(GLBufferObject& bo, size_t size) +{ + if (size == 0) + { + QD3D12_ResetBufferResource(bo); + return true; + } + + if (!g_gl.device) + return false; + + if (bo.resource && bo.gpuBytes >= size && bo.mappedGpu) + return true; + + if (bo.resource && bo.mappedGpu) + bo.resource->Unmap(0, nullptr); + + bo.resource.Reset(); + bo.mappedGpu = nullptr; + bo.gpuAddress = 0; + bo.gpuBytes = 0; + + D3D12_HEAP_PROPERTIES hp{}; + hp.Type = D3D12_HEAP_TYPE_UPLOAD; + + D3D12_RESOURCE_DESC rd{}; + rd.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + rd.Width = (UINT64)size; + rd.Height = 1; + rd.DepthOrArraySize = 1; + rd.MipLevels = 1; + rd.SampleDesc.Count = 1; + rd.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + + QD3D12_CHECK(g_gl.device->CreateCommittedResource( + &hp, + D3D12_HEAP_FLAG_NONE, + &rd, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&bo.resource))); + + bo.gpuAddress = bo.resource->GetGPUVirtualAddress(); + bo.gpuBytes = size; + QD3D12_CHECK(bo.resource->Map(0, nullptr, reinterpret_cast(&bo.mappedGpu))); + return bo.mappedGpu != nullptr; +} + +static void QD3D12_UpdateBufferResource(GLBufferObject& bo, GLenum target, GLsizeiptr size, const void* data, GLenum usage) +{ + bo.target = target; + bo.usage = usage; + bo.storageFlags = 0; + bo.data.resize((size_t)size); + ++bo.revision; + QD3D12_ResetPackedVertexBuffer(bo); + + if (size > 0) + { + if (data) + memcpy(bo.data.data(), data, (size_t)size); + else + memset(bo.data.data(), 0, (size_t)size); + } + + if (QD3D12_CreateUploadBufferResource(bo, (size_t)size) && size > 0) + memcpy(bo.mappedGpu, bo.data.data(), (size_t)size); +} + static TextureResource* QD3D12_FindTextureResource(GLuint id) { if (id == 0) @@ -2379,6 +2552,16 @@ static const uint8_t* QD3D12_ResolveArrayPointer(const void* ptr) return reinterpret_cast(ptr); } +static void QD3D12_CaptureArrayPointer(GLState::ClientArrayState& array, GLint size, GLenum type, GLsizei stride, const void* pointer) +{ + array.size = size; + array.type = type; + array.stride = stride; + array.ptr = QD3D12_ResolveArrayPointer(pointer); + array.buffer = g_gl.boundArrayBuffer; + array.offset = (g_gl.boundArrayBuffer != 0) ? (size_t)pointer : 0; +} + static const void* QD3D12_ResolveElementPointer(const void* ptr, GLenum indexType, GLsizei count) { if (g_gl.boundElementArrayBuffer != 0) @@ -2518,6 +2701,8 @@ cbuffer DrawCB : register(b0) float4 gTexEnvColor1; float4 gCameraPomPad; float4 gNeuralPomPad; + float4 gCurrentColor; + float4 gVertexColorPad; }; #define gUseNormalMap gMotionPad.x @@ -2531,6 +2716,7 @@ cbuffer DrawCB : register(b0) #define gCameraPomValid gCameraPomPad.w #define gUseNeuralPOM gNeuralPomPad.x #define gNeuralPOMDebug gNeuralPomPad.y +#define gUseVertexColor gVertexColorPad.x // Runtime Neural POM relief controls. x/y keep the existing enable/debug ABI; // z deepens the learned UV ray offset and w deepens the learned normal slope. // The CPU fills these from glNormalMapStrengthf() so existing material calls @@ -3688,7 +3874,7 @@ VSOut VSMain(VSIn i) : abs(currClip.z / max(abs(currClip.w), 0.00001)); o.uv0 = i.uv0; o.uv1 = i.uv1; - o.col = i.col; + o.col = (gUseVertexColor > 0.5) ? i.col : gCurrentColor; o.worldPos = worldPos.xyz; o.normal = normalize(worldNormal); o.tangent = normalize(worldTangent); @@ -4468,7 +4654,7 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out) // Direct init is much cheaper than memset + patching fields. out.px = 0.0f; out.py = 0.0f; out.pz = 0.0f; out.nx = g_gl.curNormal[0]; out.ny = g_gl.curNormal[1]; out.nz = g_gl.curNormal[2]; - out.r = 1.0f; out.g = 1.0f; out.b = 1.0f; out.a = 1.0f; + out.r = g_gl.curColor[0]; out.g = g_gl.curColor[1]; out.b = g_gl.curColor[2]; out.a = g_gl.curColor[3]; out.u0 = 0.0f; out.v0 = 0.0f; out.u1 = 0.0f; out.v1 = 0.0f; out.tx = g_gl.curTangent[0]; out.ty = g_gl.curTangent[1]; out.tz = g_gl.curTangent[2]; @@ -4480,34 +4666,31 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out) const auto& va = g_gl.vertexArray; if (va.enabled && va.ptr) { - const size_t typeSize = (size_t)QD3D12_TypeSize(va.type); - const size_t elemSize = (size_t)va.size * typeSize; - const size_t stride = va.stride ? (size_t)va.stride : elemSize; - const uint8_t* p = va.ptr + stride * (size_t)idx; - - switch (va.type) + if (va.type == GL_FLOAT) { - case GL_FLOAT: - { - const float* f = (const float*)p; + const size_t stride = va.stride ? (size_t)va.stride : (size_t)va.size * sizeof(float); + const float* f = (const float*)(va.ptr + stride * (size_t)idx); if (va.size > 0) out.px = f[0]; if (va.size > 1) out.py = f[1]; if (va.size > 2) out.pz = f[2]; - break; } - case GL_DOUBLE: + else if (va.type == GL_DOUBLE) { - const double* f = (const double*)p; + const size_t stride = va.stride ? (size_t)va.stride : (size_t)va.size * sizeof(double); + const double* f = (const double*)(va.ptr + stride * (size_t)idx); if (va.size > 0) out.px = (float)f[0]; if (va.size > 1) out.py = (float)f[1]; if (va.size > 2) out.pz = (float)f[2]; - break; } - default: + else + { + const size_t typeSize = (size_t)QD3D12_TypeSize(va.type); + const size_t elemSize = (size_t)va.size * typeSize; + const size_t stride = va.stride ? (size_t)va.stride : elemSize; + const uint8_t* p = va.ptr + stride * (size_t)idx; if (va.size > 0) out.px = QD3D12_ReadScalarFast(p + 0 * typeSize, va.type); if (va.size > 1) out.py = QD3D12_ReadScalarFast(p + 1 * typeSize, va.type); if (va.size > 2) out.pz = QD3D12_ReadScalarFast(p + 2 * typeSize, va.type); - break; } } @@ -4517,33 +4700,30 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out) const auto& na = g_gl.normalArray; if (na.enabled && na.ptr) { - const size_t typeSize = (size_t)QD3D12_TypeSize(na.type); - const size_t stride = na.stride ? (size_t)na.stride : (3 * typeSize); - const uint8_t* p = na.ptr + stride * (size_t)idx; - - switch (na.type) + if (na.type == GL_FLOAT) { - case GL_FLOAT: - { - const float* f = (const float*)p; + const size_t stride = na.stride ? (size_t)na.stride : 3 * sizeof(float); + const float* f = (const float*)(na.ptr + stride * (size_t)idx); out.nx = f[0]; out.ny = f[1]; out.nz = f[2]; - break; } - case GL_DOUBLE: + else if (na.type == GL_DOUBLE) { - const double* f = (const double*)p; + const size_t stride = na.stride ? (size_t)na.stride : 3 * sizeof(double); + const double* f = (const double*)(na.ptr + stride * (size_t)idx); out.nx = (float)f[0]; out.ny = (float)f[1]; out.nz = (float)f[2]; - break; } - default: + else + { + const size_t typeSize = (size_t)QD3D12_TypeSize(na.type); + const size_t stride = na.stride ? (size_t)na.stride : (3 * typeSize); + const uint8_t* p = na.ptr + stride * (size_t)idx; out.nx = QD3D12_ReadScalarFast(p + 0 * typeSize, na.type); out.ny = QD3D12_ReadScalarFast(p + 1 * typeSize, na.type); out.nz = QD3D12_ReadScalarFast(p + 2 * typeSize, na.type); - break; } } @@ -4553,33 +4733,30 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out) const auto& ta = g_gl.tangentArray; if (ta.enabled && ta.ptr) { - const size_t typeSize = (size_t)QD3D12_TypeSize(ta.type); - const size_t stride = ta.stride ? (size_t)ta.stride : (3 * typeSize); - const uint8_t* p = ta.ptr + stride * (size_t)idx; - - switch (ta.type) + if (ta.type == GL_FLOAT) { - case GL_FLOAT: - { - const float* f = (const float*)p; + const size_t stride = ta.stride ? (size_t)ta.stride : 3 * sizeof(float); + const float* f = (const float*)(ta.ptr + stride * (size_t)idx); out.tx = f[0]; out.ty = f[1]; out.tz = f[2]; - break; } - case GL_DOUBLE: + else if (ta.type == GL_DOUBLE) { - const double* f = (const double*)p; + const size_t stride = ta.stride ? (size_t)ta.stride : 3 * sizeof(double); + const double* f = (const double*)(ta.ptr + stride * (size_t)idx); out.tx = (float)f[0]; out.ty = (float)f[1]; out.tz = (float)f[2]; - break; } - default: + else + { + const size_t typeSize = (size_t)QD3D12_TypeSize(ta.type); + const size_t stride = ta.stride ? (size_t)ta.stride : (3 * typeSize); + const uint8_t* p = ta.ptr + stride * (size_t)idx; out.tx = QD3D12_ReadScalarFast(p + 0 * typeSize, ta.type); out.ty = QD3D12_ReadScalarFast(p + 1 * typeSize, ta.type); out.tz = QD3D12_ReadScalarFast(p + 2 * typeSize, ta.type); - break; } } @@ -4589,33 +4766,30 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out) const auto& ba = g_gl.bitangentArray; if (ba.enabled && ba.ptr) { - const size_t typeSize = (size_t)QD3D12_TypeSize(ba.type); - const size_t stride = ba.stride ? (size_t)ba.stride : (3 * typeSize); - const uint8_t* p = ba.ptr + stride * (size_t)idx; - - switch (ba.type) + if (ba.type == GL_FLOAT) { - case GL_FLOAT: - { - const float* f = (const float*)p; + const size_t stride = ba.stride ? (size_t)ba.stride : 3 * sizeof(float); + const float* f = (const float*)(ba.ptr + stride * (size_t)idx); out.bx = f[0]; out.by = f[1]; out.bz = f[2]; - break; } - case GL_DOUBLE: + else if (ba.type == GL_DOUBLE) { - const double* f = (const double*)p; + const size_t stride = ba.stride ? (size_t)ba.stride : 3 * sizeof(double); + const double* f = (const double*)(ba.ptr + stride * (size_t)idx); out.bx = (float)f[0]; out.by = (float)f[1]; out.bz = (float)f[2]; - break; } - default: + else + { + const size_t typeSize = (size_t)QD3D12_TypeSize(ba.type); + const size_t stride = ba.stride ? (size_t)ba.stride : (3 * typeSize); + const uint8_t* p = ba.ptr + stride * (size_t)idx; out.bx = QD3D12_ReadScalarFast(p + 0 * typeSize, ba.type); out.by = QD3D12_ReadScalarFast(p + 1 * typeSize, ba.type); out.bz = QD3D12_ReadScalarFast(p + 2 * typeSize, ba.type); - break; } } @@ -4625,14 +4799,11 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out) const auto& ca = g_gl.colorArray; if (ca.enabled && ca.ptr) { - const size_t typeSize = (size_t)QD3D12_TypeSize(ca.type); - const size_t elemSize = (size_t)ca.size * typeSize; - const size_t stride = ca.stride ? (size_t)ca.stride : elemSize; - const uint8_t* p = ca.ptr + stride * (size_t)idx; - if (ca.type == GL_UNSIGNED_BYTE) { static const float kInv255 = 1.0f / 255.0f; + const size_t stride = ca.stride ? (size_t)ca.stride : (size_t)ca.size * sizeof(GLubyte); + const uint8_t* p = ca.ptr + stride * (size_t)idx; if (ca.size > 0) out.r = p[0] * kInv255; if (ca.size > 1) out.g = p[1] * kInv255; if (ca.size > 2) out.b = p[2] * kInv255; @@ -4640,7 +4811,8 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out) } else if (ca.type == GL_FLOAT) { - const float* f = (const float*)p; + const size_t stride = ca.stride ? (size_t)ca.stride : (size_t)ca.size * sizeof(float); + const float* f = (const float*)(ca.ptr + stride * (size_t)idx); if (ca.size > 0) out.r = f[0]; if (ca.size > 1) out.g = f[1]; if (ca.size > 2) out.b = f[2]; @@ -4648,7 +4820,8 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out) } else if (ca.type == GL_DOUBLE) { - const double* f = (const double*)p; + const size_t stride = ca.stride ? (size_t)ca.stride : (size_t)ca.size * sizeof(double); + const double* f = (const double*)(ca.ptr + stride * (size_t)idx); if (ca.size > 0) out.r = (float)f[0]; if (ca.size > 1) out.g = (float)f[1]; if (ca.size > 2) out.b = (float)f[2]; @@ -4656,6 +4829,10 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out) } else { + const size_t typeSize = (size_t)QD3D12_TypeSize(ca.type); + const size_t elemSize = (size_t)ca.size * typeSize; + const size_t stride = ca.stride ? (size_t)ca.stride : elemSize; + const uint8_t* p = ca.ptr + stride * (size_t)idx; if (ca.size > 0) out.r = QD3D12_ReadScalarFast(p + 0 * typeSize, ca.type); if (ca.size > 1) out.g = QD3D12_ReadScalarFast(p + 1 * typeSize, ca.type); if (ca.size > 2) out.b = QD3D12_ReadScalarFast(p + 2 * typeSize, ca.type); @@ -4670,31 +4847,28 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out) const auto& tc = g_gl.texCoordArray[0]; if (tc.enabled && tc.ptr) { - const size_t typeSize = (size_t)QD3D12_TypeSize(tc.type); - const size_t elemSize = (size_t)tc.size * typeSize; - const size_t stride = tc.stride ? (size_t)tc.stride : elemSize; - const uint8_t* p = tc.ptr + stride * (size_t)idx; - - switch (tc.type) + if (tc.type == GL_FLOAT) { - case GL_FLOAT: - { - const float* f = (const float*)p; + const size_t stride = tc.stride ? (size_t)tc.stride : (size_t)tc.size * sizeof(float); + const float* f = (const float*)(tc.ptr + stride * (size_t)idx); if (tc.size > 0) out.u0 = f[0]; if (tc.size > 1) out.v0 = f[1]; - break; } - case GL_DOUBLE: + else if (tc.type == GL_DOUBLE) { - const double* f = (const double*)p; + const size_t stride = tc.stride ? (size_t)tc.stride : (size_t)tc.size * sizeof(double); + const double* f = (const double*)(tc.ptr + stride * (size_t)idx); if (tc.size > 0) out.u0 = (float)f[0]; if (tc.size > 1) out.v0 = (float)f[1]; - break; } - default: + else + { + const size_t typeSize = (size_t)QD3D12_TypeSize(tc.type); + const size_t elemSize = (size_t)tc.size * typeSize; + const size_t stride = tc.stride ? (size_t)tc.stride : elemSize; + const uint8_t* p = tc.ptr + stride * (size_t)idx; if (tc.size > 0) out.u0 = QD3D12_ReadScalarFast(p + 0 * typeSize, tc.type); if (tc.size > 1) out.v0 = QD3D12_ReadScalarFast(p + 1 * typeSize, tc.type); - break; } } } @@ -4706,31 +4880,28 @@ static void QD3D12_FetchArrayVertex(GLint idx, GLVertex& out) const auto& tc = g_gl.texCoordArray[1]; if (tc.enabled && tc.ptr) { - const size_t typeSize = (size_t)QD3D12_TypeSize(tc.type); - const size_t elemSize = (size_t)tc.size * typeSize; - const size_t stride = tc.stride ? (size_t)tc.stride : elemSize; - const uint8_t* p = tc.ptr + stride * (size_t)idx; - - switch (tc.type) + if (tc.type == GL_FLOAT) { - case GL_FLOAT: - { - const float* f = (const float*)p; + const size_t stride = tc.stride ? (size_t)tc.stride : (size_t)tc.size * sizeof(float); + const float* f = (const float*)(tc.ptr + stride * (size_t)idx); if (tc.size > 0) out.u1 = f[0]; if (tc.size > 1) out.v1 = f[1]; - break; } - case GL_DOUBLE: + else if (tc.type == GL_DOUBLE) { - const double* f = (const double*)p; + const size_t stride = tc.stride ? (size_t)tc.stride : (size_t)tc.size * sizeof(double); + const double* f = (const double*)(tc.ptr + stride * (size_t)idx); if (tc.size > 0) out.u1 = (float)f[0]; if (tc.size > 1) out.v1 = (float)f[1]; - break; } - default: + else + { + const size_t typeSize = (size_t)QD3D12_TypeSize(tc.type); + const size_t elemSize = (size_t)tc.size * typeSize; + const size_t stride = tc.stride ? (size_t)tc.stride : elemSize; + const uint8_t* p = tc.ptr + stride * (size_t)idx; if (tc.size > 0) out.u1 = QD3D12_ReadScalarFast(p + 0 * typeSize, tc.type); if (tc.size > 1) out.v1 = QD3D12_ReadScalarFast(p + 1 * typeSize, tc.type); - break; } } } @@ -4861,10 +5032,8 @@ static bool QD3D12_PolygonOffsetEnabledForMode(GLenum originalMode) } } -#ifdef _DEBUG -#pragma optimize off -#endif -static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* tex0, const TextureResource* tex1, TextureResource* const* allTextures) +static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* tex0, const TextureResource* tex1, TextureResource* const* allTextures, + TextureResource* selectedNormalMap = nullptr, TextureResource* selectedGlowMap = nullptr, TextureResource* selectedSpecularMap = nullptr, bool selectedMapsValid = false) { const bool tex0IsMaterialMapOnly = QD3D12_TextureIsMaterialMapOnlyForFixedFunctionColor(tex0); const bool tex1IsMaterialMapOnly = QD3D12_TextureIsMaterialMapOnlyForFixedFunctionColor(tex1); @@ -4879,7 +5048,7 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* for (UINT i = 0; i < QD3D12_MaxTextureUnits; ++i) key.textureSrvIndex[i] = (allTextures && allTextures[i]) ? allTextures[i]->srvIndex : 0; - TextureResource* normalMap = QD3D12_SelectNormalMapTexture(allTextures); + TextureResource* normalMap = selectedMapsValid ? selectedNormalMap : QD3D12_SelectNormalMapTexture(allTextures); if (normalMap && normalMap->texture && normalMap->srvIndex != UINT_MAX && normalMap->gpuValid) { key.normalMapSrvIndex = normalMap->srvIndex; @@ -4894,7 +5063,7 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* key.normalMapYSign = g_gl.currentNormalMapYSign; key.useTessellation = false; - TextureResource* glowMap = QD3D12_SelectGlowMapTexture(allTextures); + TextureResource* glowMap = selectedMapsValid ? selectedGlowMap : QD3D12_SelectGlowMapTexture(allTextures); if (glowMap && glowMap->texture && glowMap->srvIndex != UINT_MAX) { key.glowMapSrvIndex = glowMap->srvIndex; @@ -4907,7 +5076,7 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* } key.glowMapStrength = g_gl.currentGlowMapStrength; - TextureResource* specularMap = QD3D12_SelectSpecularMapTexture(allTextures); + TextureResource* specularMap = selectedMapsValid ? selectedSpecularMap : QD3D12_SelectSpecularMapTexture(allTextures); if (specularMap && specularMap->texture && specularMap->srvIndex != UINT_MAX) { key.specularMapSrvIndex = specularMap->srvIndex; @@ -4975,7 +5144,7 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* } key.alphaRef = g_gl.alphaRef; - key.alphaFunc = MapAlphaFunc(g_gl.alphaFunc); + key.alphaFunc = g_gl.alphaFuncMapped; key.useTex0 = useTex0 ? 1.0f : 0.0f; key.useTex1 = useTex1 ? 1.0f : 0.0f; key.viewport = g_currentWindow->viewport; @@ -4998,6 +5167,11 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* key.fogColor[1] = g_gl.fogColor[1]; key.fogColor[2] = g_gl.fogColor[2]; key.fogColor[3] = g_gl.fogColor[3]; + key.currentColor[0] = g_gl.curColor[0]; + key.currentColor[1] = g_gl.curColor[1]; + key.currentColor[2] = g_gl.curColor[2]; + key.currentColor[3] = g_gl.curColor[3]; + key.useVertexColor = g_gl.colorArray.enabled ? 1.0f : 0.0f; QD3D12_FillTexCombineKey(key, 0); QD3D12_FillTexCombineKey(key, 1); key.colorWriteMask = QD3D12_CurrentColorWriteMask(); @@ -5051,11 +5225,6 @@ static BatchKey BuildCurrentBatchKey(GLenum originalMode, const TextureResource* return key; } -#ifdef _DEBUG -#pragma optimize on -#endif - - static Mat4 QD3D12_GetPreviousMVPForObject(GLuint objectId, const Mat4& currentMvp) { auto it = g_gl.prevObjectMVPs.find(objectId); @@ -10855,6 +11024,9 @@ void glLoadModelMatrixf(const float* m16) memcpy(m.m, m16, sizeof(float) * 16); } + if (memcmp(g_gl.modelMatrix.m, m.m, sizeof(g_gl.modelMatrix.m)) == 0) + return; + memcpy(g_gl.modelMatrix.m, m.m, sizeof(g_gl.modelMatrix.m)); } @@ -10887,6 +11059,128 @@ static inline void AppendVerticesFast(std::vector& dst, const std::vec memcpy(dst.data() + oldSize, src.data(), addCount * sizeof(GLVertex)); } +static GLVertex* QD3D12_AppendToQueuedBatch(QueuedBatch* batch, size_t outCount) +{ + if (!batch || outCount == 0) + return nullptr; + + size_t first = 0; + GLVertex* dst = QD3D12_AllocFrameVertices(outCount, &first); + if (!dst) + return nullptr; + + if (batch->vertexCount == 0) + { + batch->firstVertex = first; + batch->vertexCount = outCount; + } + else + { + assert(batch->firstVertex + batch->vertexCount == first); + batch->vertexCount += outCount; + } + + return dst; +} + +static QueuedBatch* QD3D12_PrepareImmediateBatch(GLenum mode, size_t n) +{ + QD3D12_EnsureFrameOpen(); + + const bool arbProgramsActive = QD3D12ARB_IsActive(); + + TextureResource* boundTextures[QD3D12_MaxTextureUnits] = {}; + for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit) + boundTextures[unit] = &g_gl.whiteTexture; + + for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit) + { + const GLuint boundTexture = g_gl.boundTexture[unit]; + const bool arbUnit = arbProgramsActive && (boundTexture != 0); + const bool fixedColorUnit = (!arbProgramsActive) && (g_gl.texture2D[unit] && unit < 2); + TextureResource* tex = boundTexture != 0 ? QD3D12_FindTextureResource(boundTexture) : nullptr; + const bool taggedNormalUnit = (!arbProgramsActive) && tex && tex->isNormalMap; + const bool explicitNormalUnit = (!arbProgramsActive) && (g_gl.currentNormalMapTexture != 0) && (boundTexture == g_gl.currentNormalMapTexture); + const bool taggedGlowUnit = (!arbProgramsActive) && tex && tex->isGlowMap; + const bool explicitGlowUnit = (!arbProgramsActive) && (g_gl.currentGlowMapTexture != 0) && (boundTexture == g_gl.currentGlowMapTexture); + const bool taggedSpecularUnit = (!arbProgramsActive) && tex && tex->isSpecularMap; + const bool explicitSpecularUnit = (!arbProgramsActive) && (g_gl.currentSpecularMapTexture != 0) && (boundTexture == g_gl.currentSpecularMapTexture); + + const bool wantsUnit = arbUnit || fixedColorUnit || taggedNormalUnit || explicitNormalUnit || taggedGlowUnit || explicitGlowUnit || taggedSpecularUnit || explicitSpecularUnit; + if (wantsUnit && tex) + boundTextures[unit] = tex; + } + + for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit) + { + TextureResource* tex = boundTextures[unit]; + if (tex && tex != &g_gl.whiteTexture && !tex->gpuValid) + { + EnsureTextureResource(*tex); + UploadTexture(*tex); + } + } + + TextureResource* normalMapTex = QD3D12_SelectNormalMapTexture(boundTextures); + if (normalMapTex && normalMapTex != &g_gl.whiteTexture && !normalMapTex->gpuValid) + { + EnsureTextureResource(*normalMapTex); + UploadTexture(*normalMapTex); + } + if (normalMapTex && normalMapTex != &g_gl.whiteTexture && QD3D12_TextureHasNeuralPOMData(*normalMapTex)) + QD3D12_UploadNeuralPOM(*normalMapTex); + + if (g_gl.currentNeuralPOMTexture != 0) + { + TextureResource* explicitNeural = QD3D12_FindTextureResource(g_gl.currentNeuralPOMTexture); + if (explicitNeural && QD3D12_TextureHasNeuralPOMData(*explicitNeural)) + QD3D12_UploadNeuralPOM(*explicitNeural); + } + + for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit) + { + TextureResource* neuralCandidate = boundTextures[unit]; + if (neuralCandidate && neuralCandidate != &g_gl.whiteTexture && QD3D12_TextureHasNeuralPOMData(*neuralCandidate)) + QD3D12_UploadNeuralPOM(*neuralCandidate); + } + + TextureResource* glowMapTex = QD3D12_SelectGlowMapTexture(boundTextures); + if (glowMapTex && glowMapTex != &g_gl.whiteTexture && !glowMapTex->gpuValid) + { + EnsureTextureResource(*glowMapTex); + UploadTexture(*glowMapTex); + } + + TextureResource* specularMapTex = QD3D12_SelectSpecularMapTexture(boundTextures); + if (specularMapTex && specularMapTex != &g_gl.whiteTexture && !specularMapTex->gpuValid) + { + EnsureTextureResource(*specularMapTex); + UploadTexture(*specularMapTex); + } + + BatchKey key = BuildCurrentBatchKey(mode, boundTextures[0], boundTextures[1], boundTextures, normalMapTex, glowMapTex, specularMapTex, true); + if (key.useTessellation && !QD3D12_ImmediateVertexCountCanUseNormalMapTessellation(mode, n)) + key.useTessellation = false; + + const size_t markerCursor = g_gl.queryMarkers.size(); + if (!g_gl.queuedBatches.empty() && + !g_gl.queuedBatches.back().gpuIndexed && + BatchKeyEquals(g_gl.queuedBatches.back().key, key) && + g_gl.queuedBatches.back().markerEnd == markerCursor) + { + return &g_gl.queuedBatches.back(); + } + + QueuedBatch newBatch{}; + newBatch.key = key; + newBatch.markerBegin = markerCursor; + newBatch.markerEnd = markerCursor; + newBatch.firstVertex = 0; + newBatch.vertexCount = 0; + g_gl.queuedBatches.push_back(newBatch); + return &g_gl.queuedBatches.back(); +} + static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n) { if (!src || n == 0) @@ -10904,27 +11198,25 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n) for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit) { + const GLuint boundTexture = g_gl.boundTexture[unit]; const bool arbUnit = arbProgramsActive && (g_gl.boundTexture[unit] != 0); const bool fixedColorUnit = (!arbProgramsActive) && (g_gl.texture2D[unit] && unit < 2); - const bool taggedNormalUnit = (!arbProgramsActive) && QD3D12_IsTextureTaggedNormalMap(g_gl.boundTexture[unit]); + TextureResource* tex = boundTexture != 0 ? QD3D12_FindTextureResource(boundTexture) : nullptr; + const bool taggedNormalUnit = (!arbProgramsActive) && tex && tex->isNormalMap; const bool explicitNormalUnit = (!arbProgramsActive) && (g_gl.currentNormalMapTexture != 0) && - (g_gl.boundTexture[unit] == g_gl.currentNormalMapTexture); - const bool taggedGlowUnit = (!arbProgramsActive) && QD3D12_IsTextureTaggedGlowMap(g_gl.boundTexture[unit]); + (boundTexture == g_gl.currentNormalMapTexture); + const bool taggedGlowUnit = (!arbProgramsActive) && tex && tex->isGlowMap; const bool explicitGlowUnit = (!arbProgramsActive) && (g_gl.currentGlowMapTexture != 0) && - (g_gl.boundTexture[unit] == g_gl.currentGlowMapTexture); - const bool taggedSpecularUnit = (!arbProgramsActive) && QD3D12_IsTextureTaggedSpecularMap(g_gl.boundTexture[unit]); + (boundTexture == g_gl.currentGlowMapTexture); + const bool taggedSpecularUnit = (!arbProgramsActive) && tex && tex->isSpecularMap; const bool explicitSpecularUnit = (!arbProgramsActive) && (g_gl.currentSpecularMapTexture != 0) && - (g_gl.boundTexture[unit] == g_gl.currentSpecularMapTexture); + (boundTexture == g_gl.currentSpecularMapTexture); const bool wantsUnit = arbUnit || fixedColorUnit || taggedNormalUnit || explicitNormalUnit || taggedGlowUnit || explicitGlowUnit || taggedSpecularUnit || explicitSpecularUnit; - if (!wantsUnit) - continue; - - TextureResource* tex = QD3D12_FindTextureResource(g_gl.boundTexture[unit]); - if (tex) + if (wantsUnit && tex) boundTextures[unit] = tex; } @@ -10982,7 +11274,7 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n) TextureResource* tex0 = boundTextures[0]; TextureResource* tex1 = boundTextures[1]; - BatchKey key = BuildCurrentBatchKey(mode, tex0, tex1, boundTextures); + BatchKey key = BuildCurrentBatchKey(mode, tex0, tex1, boundTextures, normalMapTex, glowMapTex, specularMapTex, true); if (key.useTessellation && !QD3D12_ImmediateVertexCountCanUseNormalMapTessellation(mode, n)) key.useTessellation = false; const size_t markerCursor = g_gl.queryMarkers.size(); @@ -10990,6 +11282,7 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n) QueuedBatch* batch = nullptr; if (!g_gl.queuedBatches.empty() && + !g_gl.queuedBatches.back().gpuIndexed && BatchKeyEquals(g_gl.queuedBatches.back().key, key) && g_gl.queuedBatches.back().markerEnd == markerCursor) { @@ -11007,30 +11300,9 @@ static void FlushImmediate(GLenum mode, const GLVertex* src, size_t n) batch = &g_gl.queuedBatches.back(); } - auto AppendToBatch = [&](size_t outCount) -> GLVertex* - { - if (outCount == 0) - return nullptr; - - size_t first = 0; - GLVertex* dst = QD3D12_AllocFrameVertices(outCount, &first); - if (!dst) - return nullptr; - - if (batch->vertexCount == 0) - { - batch->firstVertex = first; - batch->vertexCount = outCount; - } - else - { - // Since arena is linear, merged batches must remain contiguous. - assert(batch->firstVertex + batch->vertexCount == first); - batch->vertexCount += outCount; - } - - return dst; - }; + auto AppendToBatch = [&](size_t outCount) -> GLVertex* { + return QD3D12_AppendToQueuedBatch(batch, outCount); + }; switch (mode) { @@ -11302,8 +11574,7 @@ static void QD3D12_FlushQueuedBatches() for (size_t i = 0; i < g_gl.queuedBatches.size(); ++i) { const QueuedBatch& batch = g_gl.queuedBatches[i]; - const GLVertex* verts = QD3D12_GetBatchVertices(batch); - const size_t count = batch.vertexCount; + const size_t count = batch.gpuIndexed ? (size_t)batch.gpuIndexCount : batch.vertexCount; if (count <= 0) continue; @@ -11329,7 +11600,7 @@ static void QD3D12_FlushQueuedBatches() g_gl.sceneFogCameraValid = batch.key.cameraValid; } - const UINT vbBytes = (UINT)(count * sizeof(GLVertex)); + const UINT vbBytes = batch.gpuIndexed ? 0 : (UINT)(count * sizeof(GLVertex)); const UINT cbBytes = (UINT)sizeof(DrawConstants); if (QD3D12_EnsureUploadSpaceForDraw(vbBytes, cbBytes)) @@ -11355,8 +11626,13 @@ static void QD3D12_FlushQueuedBatches() g_gl.cmdList->RSSetViewports(1, &batch.key.viewport); g_gl.cmdList->RSSetScissorRects(1, &batch.key.scissor); - UploadAlloc vbAlloc = QD3D12_AllocUpload(vbBytes, 256); - memcpy(vbAlloc.cpu, verts, vbBytes); + UploadAlloc vbAlloc{}; + if (!batch.gpuIndexed) + { + const GLVertex* verts = QD3D12_GetBatchVertices(batch); + vbAlloc = QD3D12_AllocUpload(vbBytes, 256); + memcpy(vbAlloc.cpu, verts, vbBytes); + } UploadAlloc cbAlloc = QD3D12_AllocUpload(cbBytes, 256); DrawConstants* dc = reinterpret_cast(cbAlloc.cpu); @@ -11432,6 +11708,11 @@ static void QD3D12_FlushQueuedBatches() const float neuralStrength = ClampValue(batch.key.normalMapStrength, 0.0f, 4.0f); dc->neuralPomPad[2] = ClampValue(1.0f + (neuralStrength - 1.0f) * 0.65f, 1.0f, 3.0f); dc->neuralPomPad[3] = ClampValue(1.0f + (neuralStrength - 1.0f) * 0.45f, 1.0f, 2.5f); + memcpy(dc->currentColor, batch.key.currentColor, sizeof(dc->currentColor)); + dc->vertexColorPad[0] = batch.key.useVertexColor; + dc->vertexColorPad[1] = 0.0f; + dc->vertexColorPad[2] = 0.0f; + dc->vertexColorPad[3] = 0.0f; if (batch.key.useARBPrograms) { @@ -11441,9 +11722,16 @@ static void QD3D12_FlushQueuedBatches() } D3D12_VERTEX_BUFFER_VIEW vbv{}; - vbv.BufferLocation = vbAlloc.gpu; - vbv.SizeInBytes = (UINT)(count * sizeof(GLVertex)); - vbv.StrideInBytes = sizeof(GLVertex); + if (batch.gpuIndexed) + { + vbv = batch.gpuVbv; + } + else + { + vbv.BufferLocation = vbAlloc.gpu; + vbv.SizeInBytes = (UINT)(count * sizeof(GLVertex)); + vbv.StrideInBytes = sizeof(GLVertex); + } ID3D12PipelineState* pso = nullptr; const D3D12_PRIMITIVE_TOPOLOGY_TYPE topoType = @@ -11586,7 +11874,16 @@ static void QD3D12_FlushQueuedBatches() } g_gl.cmdList->IASetVertexBuffers(0, 1, &vbv); - g_gl.cmdList->DrawInstanced((UINT)count, 1, 0, 0); + if (batch.gpuIndexed) + { + g_gl.cmdList->IASetIndexBuffer(&batch.gpuIbv); + g_gl.cmdList->DrawIndexedInstanced(batch.gpuIndexCount, 1, 0, 0, 0); + } + else + { + g_gl.cmdList->IASetIndexBuffer(nullptr); + g_gl.cmdList->DrawInstanced((UINT)count, 1, 0, 0); + } } g_gl.queuedBatches.clear(); @@ -11846,6 +12143,7 @@ void APIENTRY glBlendFunc(GLenum sfactor, GLenum dfactor) void APIENTRY glAlphaFunc(GLenum func, GLclampf ref) { g_gl.alphaFunc = func; + g_gl.alphaFuncMapped = MapAlphaFunc(func); g_gl.alphaRef = ref; } @@ -12098,11 +12396,11 @@ void APIENTRY glDeleteTextures(GLsizei n, const GLuint* textures) } } -#ifdef _DEBUG -#pragma optimize off -#endif void APIENTRY glBindTexture(GLenum, GLuint texture) { + if (g_gl.boundTexture[g_gl.activeTextureUnit] == texture) + return; + g_gl.boundTexture[g_gl.activeTextureUnit] = texture; if (texture == 0) @@ -12130,6 +12428,9 @@ void APIENTRY glTextureNormalMap(GLuint texture, GLboolean isNormalMap) void APIENTRY glBindNormalMapTexture(GLuint texture) { + if (g_gl.currentNormalMapTexture == texture) + return; + if (texture != 0) (void)QD3D12_EnsureTextureName(texture); @@ -12143,7 +12444,10 @@ void APIENTRY glNormalMapTexture(GLuint texture) void APIENTRY glNormalMapStrengthf(GLfloat strength) { - g_gl.currentNormalMapStrength = (strength < 0.0f) ? 0.0f : strength; + const float clamped = (strength < 0.0f) ? 0.0f : strength; + if (g_gl.currentNormalMapStrength == clamped) + return; + g_gl.currentNormalMapStrength = clamped; } void APIENTRY glNormalMapYSignf(GLfloat sign) @@ -12332,6 +12636,9 @@ void APIENTRY glClearNeuralPOMMaterialQD3D12(GLuint texture) void APIENTRY glBindNeuralPOMTextureQD3D12(GLuint texture) { + if (g_gl.currentNeuralPOMTexture == texture) + return; + if (texture != 0) (void)QD3D12_EnsureTextureName(texture); g_gl.currentNeuralPOMTexture = texture; @@ -12366,6 +12673,9 @@ void APIENTRY glTextureGlowMap(GLuint texture, GLboolean isGlowMap) void APIENTRY glBindGlowMapTexture(GLuint texture) { + if (g_gl.currentGlowMapTexture == texture) + return; + if (texture != 0) { TextureResource& tex = QD3D12_EnsureTextureName(texture); @@ -12387,7 +12697,10 @@ void APIENTRY glGlowMapTexture(GLuint texture) void APIENTRY glGlowMapStrengthf(GLfloat strength) { - g_gl.currentGlowMapStrength = (strength < 0.0f) ? 0.0f : strength; + const float clamped = (strength < 0.0f) ? 0.0f : strength; + if (g_gl.currentGlowMapStrength == clamped) + return; + g_gl.currentGlowMapStrength = clamped; } void APIENTRY glTagTextureSpecularMap(GLuint texture, GLboolean isSpecularMap) @@ -12409,6 +12722,9 @@ void APIENTRY glTextureSpecularMap(GLuint texture, GLboolean isSpecularMap) void APIENTRY glBindSpecularMapTexture(GLuint texture) { + if (g_gl.currentSpecularMapTexture == texture) + return; + if (texture != 0) { TextureResource& tex = QD3D12_EnsureTextureName(texture); @@ -12456,9 +12772,6 @@ void APIENTRY glMaterialGlassQD3D12(GLboolean enable) { glGlassMaterialQD3D12(enable); } -#ifdef _DEBUG -#pragma optimize on -#endif void APIENTRY glLoadMatrixf(const GLfloat* m) { @@ -14015,26 +14328,50 @@ void APIENTRY glUnlockArraysEXT(void) { void APIENTRY glNormalPointer(GLenum type, GLsizei stride, const void* pointer) { - g_gl.normalArray.size = 3; - g_gl.normalArray.type = type; - g_gl.normalArray.stride = stride; - g_gl.normalArray.ptr = QD3D12_ResolveArrayPointer(pointer); + const uint8_t* resolved = QD3D12_ResolveArrayPointer(pointer); + const GLuint buffer = g_gl.boundArrayBuffer; + const size_t offset = buffer != 0 ? (size_t)pointer : 0; + if (g_gl.normalArray.size == 3 && + g_gl.normalArray.type == type && + g_gl.normalArray.stride == stride && + g_gl.normalArray.ptr == resolved && + g_gl.normalArray.buffer == buffer && + g_gl.normalArray.offset == offset) + return; + + QD3D12_CaptureArrayPointer(g_gl.normalArray, 3, type, stride, pointer); } void APIENTRY glTangentPointer(GLenum type, GLsizei stride, const void* pointer) { - g_gl.tangentArray.size = 3; - g_gl.tangentArray.type = type; - g_gl.tangentArray.stride = stride; - g_gl.tangentArray.ptr = QD3D12_ResolveArrayPointer(pointer); + const uint8_t* resolved = QD3D12_ResolveArrayPointer(pointer); + const GLuint buffer = g_gl.boundArrayBuffer; + const size_t offset = buffer != 0 ? (size_t)pointer : 0; + if (g_gl.tangentArray.size == 3 && + g_gl.tangentArray.type == type && + g_gl.tangentArray.stride == stride && + g_gl.tangentArray.ptr == resolved && + g_gl.tangentArray.buffer == buffer && + g_gl.tangentArray.offset == offset) + return; + + QD3D12_CaptureArrayPointer(g_gl.tangentArray, 3, type, stride, pointer); } void APIENTRY glBinormalPointer(GLenum type, GLsizei stride, const void* pointer) { - g_gl.bitangentArray.size = 3; - g_gl.bitangentArray.type = type; - g_gl.bitangentArray.stride = stride; - g_gl.bitangentArray.ptr = QD3D12_ResolveArrayPointer(pointer); + const uint8_t* resolved = QD3D12_ResolveArrayPointer(pointer); + const GLuint buffer = g_gl.boundArrayBuffer; + const size_t offset = buffer != 0 ? (size_t)pointer : 0; + if (g_gl.bitangentArray.size == 3 && + g_gl.bitangentArray.type == type && + g_gl.bitangentArray.stride == stride && + g_gl.bitangentArray.ptr == resolved && + g_gl.bitangentArray.buffer == buffer && + g_gl.bitangentArray.offset == offset) + return; + + QD3D12_CaptureArrayPointer(g_gl.bitangentArray, 3, type, stride, pointer); } void APIENTRY glNormalBuffer(GLenum type, GLsizei stride, const void* pointer) @@ -14060,21 +14397,33 @@ void APIENTRY glEnableClientState(GLenum array) switch (array) { case GL_VERTEX_ARRAY: + if (g_gl.vertexArray.enabled) + return; g_gl.vertexArray.enabled = true; break; case GL_NORMAL_ARRAY: + if (g_gl.normalArray.enabled) + return; g_gl.normalArray.enabled = true; break; case GL_TANGENT_ARRAY_QD3D12: + if (g_gl.tangentArray.enabled) + return; g_gl.tangentArray.enabled = true; break; case GL_BINORMAL_ARRAY_QD3D12: + if (g_gl.bitangentArray.enabled) + return; g_gl.bitangentArray.enabled = true; break; case GL_COLOR_ARRAY: + if (g_gl.colorArray.enabled) + return; g_gl.colorArray.enabled = true; break; case GL_TEXTURE_COORD_ARRAY: + if (g_gl.texCoordArray[g_gl.clientActiveTextureUnit].enabled) + return; g_gl.texCoordArray[g_gl.clientActiveTextureUnit].enabled = true; break; default: @@ -14087,21 +14436,33 @@ void APIENTRY glDisableClientState(GLenum array) switch (array) { case GL_VERTEX_ARRAY: + if (!g_gl.vertexArray.enabled) + return; g_gl.vertexArray.enabled = false; break; case GL_NORMAL_ARRAY: + if (!g_gl.normalArray.enabled) + return; g_gl.normalArray.enabled = false; break; case GL_TANGENT_ARRAY_QD3D12: + if (!g_gl.tangentArray.enabled) + return; g_gl.tangentArray.enabled = false; break; case GL_BINORMAL_ARRAY_QD3D12: + if (!g_gl.bitangentArray.enabled) + return; g_gl.bitangentArray.enabled = false; break; case GL_COLOR_ARRAY: + if (!g_gl.colorArray.enabled) + return; g_gl.colorArray.enabled = false; break; case GL_TEXTURE_COORD_ARRAY: + if (!g_gl.texCoordArray[g_gl.clientActiveTextureUnit].enabled) + return; g_gl.texCoordArray[g_gl.clientActiveTextureUnit].enabled = false; break; default: @@ -14111,27 +14472,51 @@ void APIENTRY glDisableClientState(GLenum array) void APIENTRY glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* ptr) { - g_gl.vertexArray.size = size; - g_gl.vertexArray.type = type; - g_gl.vertexArray.stride = stride; - g_gl.vertexArray.ptr = QD3D12_ResolveArrayPointer(ptr); + const uint8_t* resolved = QD3D12_ResolveArrayPointer(ptr); + const GLuint buffer = g_gl.boundArrayBuffer; + const size_t offset = buffer != 0 ? (size_t)ptr : 0; + if (g_gl.vertexArray.size == size && + g_gl.vertexArray.type == type && + g_gl.vertexArray.stride == stride && + g_gl.vertexArray.ptr == resolved && + g_gl.vertexArray.buffer == buffer && + g_gl.vertexArray.offset == offset) + return; + + QD3D12_CaptureArrayPointer(g_gl.vertexArray, size, type, stride, ptr); } void APIENTRY glColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* ptr) { - g_gl.colorArray.size = size; - g_gl.colorArray.type = type; - g_gl.colorArray.stride = stride; - g_gl.colorArray.ptr = QD3D12_ResolveArrayPointer(ptr); + const uint8_t* resolved = QD3D12_ResolveArrayPointer(ptr); + const GLuint buffer = g_gl.boundArrayBuffer; + const size_t offset = buffer != 0 ? (size_t)ptr : 0; + if (g_gl.colorArray.size == size && + g_gl.colorArray.type == type && + g_gl.colorArray.stride == stride && + g_gl.colorArray.ptr == resolved && + g_gl.colorArray.buffer == buffer && + g_gl.colorArray.offset == offset) + return; + + QD3D12_CaptureArrayPointer(g_gl.colorArray, size, type, stride, ptr); } void APIENTRY glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GLvoid* ptr) { auto& tc = g_gl.texCoordArray[g_gl.clientActiveTextureUnit]; - tc.size = size; - tc.type = type; - tc.stride = stride; - tc.ptr = QD3D12_ResolveArrayPointer(ptr); + const uint8_t* resolved = QD3D12_ResolveArrayPointer(ptr); + const GLuint buffer = g_gl.boundArrayBuffer; + const size_t offset = buffer != 0 ? (size_t)ptr : 0; + if (tc.size == size && + tc.type == type && + tc.stride == stride && + tc.ptr == resolved && + tc.buffer == buffer && + tc.offset == offset) + return; + + QD3D12_CaptureArrayPointer(tc, size, type, stride, ptr); } void APIENTRY glArrayElement(GLint i) @@ -14140,11 +14525,205 @@ void APIENTRY glArrayElement(GLint i) QD3D12_FetchArrayVertex(i, v); } +static bool QD3D12_ArrayUsesOnlyBuffer(const GLState::ClientArrayState& array, GLuint buffer) +{ + return !array.enabled || array.buffer == buffer; +} + +static bool QD3D12_CanPackCurrentVertexBuffer(GLuint vertexBuffer) +{ + if (vertexBuffer == 0 || !g_gl.vertexArray.enabled || g_gl.vertexArray.buffer != vertexBuffer) + return false; + + if (g_gl.vertexArray.type != GL_FLOAT || g_gl.vertexArray.size < 3) + return false; + + if (!QD3D12_ArrayUsesOnlyBuffer(g_gl.normalArray, vertexBuffer) || + !QD3D12_ArrayUsesOnlyBuffer(g_gl.tangentArray, vertexBuffer) || + !QD3D12_ArrayUsesOnlyBuffer(g_gl.bitangentArray, vertexBuffer) || + !QD3D12_ArrayUsesOnlyBuffer(g_gl.colorArray, vertexBuffer)) + return false; + + for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit) + { + if (!QD3D12_ArrayUsesOnlyBuffer(g_gl.texCoordArray[unit], vertexBuffer)) + return false; + } + + return true; +} + +static uint64_t QD3D12_HashClientArrayLayout(const GLState::ClientArrayState& array, uint64_t hash) +{ + hash ^= (uint64_t)array.enabled + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2); + hash ^= (uint64_t)array.size + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2); + hash ^= (uint64_t)array.type + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2); + hash ^= (uint64_t)array.stride + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2); + hash ^= (uint64_t)array.buffer + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2); + hash ^= (uint64_t)array.offset + 0x9e3779b97f4a7c15ull + (hash << 6) + (hash >> 2); + return hash; +} + +static uint64_t QD3D12_CurrentClientArrayLayoutHash() +{ + uint64_t hash = 1469598103934665603ull; + hash = QD3D12_HashClientArrayLayout(g_gl.vertexArray, hash); + hash = QD3D12_HashClientArrayLayout(g_gl.normalArray, hash); + hash = QD3D12_HashClientArrayLayout(g_gl.tangentArray, hash); + hash = QD3D12_HashClientArrayLayout(g_gl.bitangentArray, hash); + hash = QD3D12_HashClientArrayLayout(g_gl.colorArray, hash); + for (UINT unit = 0; unit < QD3D12_MaxTextureUnits; ++unit) + hash = QD3D12_HashClientArrayLayout(g_gl.texCoordArray[unit], hash); + return hash; +} + +static bool QD3D12_EnsurePackedVertexBuffer(GLBufferObject& bo) +{ + if (bo.usage == GL_STREAM_DRAW_ARB || bo.data.empty()) + return false; + + if (!QD3D12_CanPackCurrentVertexBuffer(bo.id)) + return false; + + const GLsizei strideValue = g_gl.vertexArray.stride; + const size_t vertexTypeSize = QD3D12_TypeSize(g_gl.vertexArray.type); + const size_t vertexStride = strideValue ? (size_t)strideValue : (size_t)g_gl.vertexArray.size * vertexTypeSize; + if (vertexStride == 0 || g_gl.vertexArray.offset >= bo.data.size()) + return false; + + const size_t vertexCount = (bo.data.size() - g_gl.vertexArray.offset) / vertexStride; + const size_t packedBytes = vertexCount * sizeof(GLVertex); + if (vertexCount == 0 || packedBytes == 0) + return false; + + const uint64_t layoutHash = QD3D12_CurrentClientArrayLayoutHash(); + if (bo.packedVertexResource && + bo.packedVertexMapped && + bo.packedVertexRevision == bo.revision && + bo.packedVertexLayoutHash == layoutHash && + bo.packedVertexCount == vertexCount && + bo.packedVertexBytes >= packedBytes) + { + return true; + } + + QD3D12_ResetPackedVertexBuffer(bo); + + if (!g_gl.device) + return false; + + D3D12_HEAP_PROPERTIES hp{}; + hp.Type = D3D12_HEAP_TYPE_UPLOAD; + + D3D12_RESOURCE_DESC rd{}; + rd.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + rd.Width = (UINT64)packedBytes; + rd.Height = 1; + rd.DepthOrArraySize = 1; + rd.MipLevels = 1; + rd.SampleDesc.Count = 1; + rd.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + + QD3D12_CHECK(g_gl.device->CreateCommittedResource( + &hp, + D3D12_HEAP_FLAG_NONE, + &rd, + D3D12_RESOURCE_STATE_GENERIC_READ, + nullptr, + IID_PPV_ARGS(&bo.packedVertexResource))); + + bo.packedVertexGpuAddress = bo.packedVertexResource->GetGPUVirtualAddress(); + bo.packedVertexBytes = packedBytes; + bo.packedVertexCount = vertexCount; + QD3D12_CHECK(bo.packedVertexResource->Map(0, nullptr, reinterpret_cast(&bo.packedVertexMapped))); + + GLVertex* packed = reinterpret_cast(bo.packedVertexMapped); + for (size_t i = 0; i < vertexCount; ++i) + QD3D12_FetchArrayVertex((GLint)i, packed[i]); + + bo.packedVertexRevision = bo.revision; + bo.packedVertexLayoutHash = layoutHash; + return true; +} + +static DXGI_FORMAT QD3D12_IndexFormatFromGL(GLenum type) +{ + switch (type) + { + case GL_UNSIGNED_INT: return DXGI_FORMAT_R32_UINT; + case GL_UNSIGNED_SHORT: return DXGI_FORMAT_R16_UINT; + default: return DXGI_FORMAT_UNKNOWN; + } +} + +static size_t QD3D12_IndexSizeFromGL(GLenum type) +{ + switch (type) + { + case GL_UNSIGNED_INT: return sizeof(GLuint); + case GL_UNSIGNED_SHORT: return sizeof(GLushort); + case GL_UNSIGNED_BYTE: return sizeof(GLubyte); + default: return 0; + } +} + +static bool QD3D12_TryQueueBufferedDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) +{ + if (mode != GL_TRIANGLES || type == GL_UNSIGNED_BYTE) + return false; + if (QD3D12ARB_IsActive()) + return false; + + GLBufferObject* vertexBo = QD3D12_GetBuffer(g_gl.vertexArray.buffer); + GLBufferObject* indexBo = QD3D12_GetBuffer(g_gl.boundElementArrayBuffer); + if (!vertexBo || !indexBo || !indexBo->resource || !indexBo->mappedGpu) + return false; + + if (!QD3D12_EnsurePackedVertexBuffer(*vertexBo)) + return false; + + const size_t indexSize = QD3D12_IndexSizeFromGL(type); + const size_t indexOffset = (g_gl.boundElementArrayBuffer != 0) ? (size_t)indices : 0; + const size_t indexBytes = (size_t)count * indexSize; + if (indexSize == 0 || indexOffset > indexBo->gpuBytes || indexBytes > (indexBo->gpuBytes - indexOffset)) + return false; + + QueuedBatch* batch = QD3D12_PrepareImmediateBatch(mode, (size_t)count); + if (!batch || batch->vertexCount != 0 || batch->gpuIndexed) + { + QueuedBatch newBatch{}; + newBatch.key = batch ? batch->key : BuildCurrentBatchKey(mode, nullptr, nullptr, nullptr); + newBatch.markerBegin = g_gl.queryMarkers.size(); + newBatch.markerEnd = newBatch.markerBegin; + g_gl.queuedBatches.push_back(newBatch); + batch = &g_gl.queuedBatches.back(); + } + + batch->gpuIndexed = true; + batch->gpuVertexResource = vertexBo->packedVertexResource; + batch->gpuIndexResource = indexBo->resource; + batch->gpuIndexCount = (UINT)count; + batch->vertexCount = (size_t)count; + batch->gpuVbv.BufferLocation = vertexBo->packedVertexGpuAddress; + batch->gpuVbv.SizeInBytes = (UINT)vertexBo->packedVertexBytes; + batch->gpuVbv.StrideInBytes = sizeof(GLVertex); + batch->gpuIbv.BufferLocation = indexBo->gpuAddress + indexOffset; + batch->gpuIbv.SizeInBytes = (UINT)indexBytes; + batch->gpuIbv.Format = QD3D12_IndexFormatFromGL(type); + return true; +} + void APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) { if (count <= 0) return; + if (type != GL_UNSIGNED_INT && type != GL_UNSIGNED_SHORT && type != GL_UNSIGNED_BYTE) + { + g_gl.lastError = GL_INVALID_ENUM; + return; + } + const void* resolvedIndices = QD3D12_ResolveElementPointer(indices, type, count); if (!resolvedIndices) { @@ -14154,7 +14733,47 @@ void APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvo g_gl.currentPrim = mode; - g_gl.immediateVerts.Clear(); + if (QD3D12_TryQueueBufferedDrawElements(mode, count, type, indices)) + return; + + if (mode == GL_TRIANGLES) + { + QueuedBatch* batch = QD3D12_PrepareImmediateBatch(mode, static_cast(count)); + GLVertex* outVerts = QD3D12_AppendToQueuedBatch(batch, static_cast(count)); + if (!outVerts) + return; + + switch (type) + { + case GL_UNSIGNED_INT: + { + const GLuint* idx = static_cast(resolvedIndices); + for (GLsizei i = 0; i < count; ++i) + QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]); + return; + } + + case GL_UNSIGNED_SHORT: + { + const GLushort* idx = static_cast(resolvedIndices); + for (GLsizei i = 0; i < count; ++i) + QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]); + return; + } + + case GL_UNSIGNED_BYTE: + { + const GLubyte* idx = static_cast(resolvedIndices); + for (GLsizei i = 0; i < count; ++i) + QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]); + return; + } + } + } + + GLVertex* outVerts = g_gl.immediateVerts.ResizeForWrite(static_cast(count)); + if (!outVerts) + return; switch (type) { @@ -14163,8 +14782,7 @@ void APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvo const GLuint* idx = static_cast(resolvedIndices); for (GLsizei i = 0; i < count; ++i) { - GLVertex& v = g_gl.immediateVerts.Push(); - QD3D12_FetchArrayVertex(static_cast(idx[i]), v); + QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]); } break; } @@ -14174,8 +14792,7 @@ void APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvo const GLushort* idx = static_cast(resolvedIndices); for (GLsizei i = 0; i < count; ++i) { - GLVertex& v = g_gl.immediateVerts.Push(); - QD3D12_FetchArrayVertex(static_cast(idx[i]), v); + QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]); } break; } @@ -14185,29 +14802,17 @@ void APIENTRY glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvo const GLubyte* idx = static_cast(resolvedIndices); for (GLsizei i = 0; i < count; ++i) { - GLVertex& v = g_gl.immediateVerts.Push(); - QD3D12_FetchArrayVertex(static_cast(idx[i]), v); + QD3D12_FetchArrayVertex(static_cast(idx[i]), outVerts[i]); } break; } default: g_gl.lastError = GL_INVALID_ENUM; + g_gl.immediateVerts.Clear(); return; } - if (!g_gl.colorArray.enabled) - { - for (int i = 0; i < g_gl.immediateVerts.Size(); i++) - { - g_gl.immediateVerts.Data()[i].r = g_gl.curColor[0]; - g_gl.immediateVerts.Data()[i].g = g_gl.curColor[1]; - g_gl.immediateVerts.Data()[i].b = g_gl.curColor[2]; - g_gl.immediateVerts.Data()[i].a = g_gl.curColor[3]; - } - } - - FlushImmediate(mode, g_gl.immediateVerts.Data(), g_gl.immediateVerts.Size()); } @@ -15868,8 +16473,6 @@ void glLightScene(glRaytracingSceneHandle_t sceneHandle) if (!tlas) return; - glRaytracingBuildSceneForHandle(sceneHandle); - QD3D12_FlushQueuedBatches(); QD3D12_ResolveGBufferForCurrentFrame(*window); @@ -16122,6 +16725,8 @@ ID3D12Resource* QD3D12_GetCurrentBackBuffer() void APIENTRY glGeometryFlagf(GLfloat flag) { + if (g_gl.currentGeometryFlag == flag) + return; g_gl.currentGeometryFlag = flag; } @@ -16317,7 +16922,10 @@ static float QD3D12_ClampToneMapBrightness(float brightness) void QD3D12_SetToneMapBrightness(float brightness) { - g_gl.toneMapBrightness = QD3D12_ClampToneMapBrightness(brightness); + const float clamped = QD3D12_ClampToneMapBrightness(brightness); + if (g_gl.toneMapBrightness == clamped) + return; + g_gl.toneMapBrightness = clamped; } float QD3D12_GetToneMapBrightness(void) @@ -16504,6 +17112,10 @@ void APIENTRY glDeleteBuffers(GLsizei n, const GLuint* buffers) if (g_gl.boundElementArrayBuffer == id) g_gl.boundElementArrayBuffer = 0; + auto it = g_gl.buffers.find(id); + if (it != g_gl.buffers.end()) + QD3D12_ResetBufferResource(it->second); + g_gl.buffers.erase(id); } } @@ -16582,23 +17194,49 @@ void APIENTRY glBufferStorage(GLenum target, GLsizeiptr size, const void* data, return; } - bo->target = target; + QD3D12_UpdateBufferResource(*bo, target, size, data, GL_STATIC_DRAW_ARB); bo->storageFlags = flags; - bo->data.resize((size_t)size); - - if (size > 0) - { - if (data) - memcpy(bo->data.data(), data, (size_t)size); - else - memset(bo->data.data(), 0, (size_t)size); - } } void APIENTRY glBufferData(GLenum target, GLsizeiptr size, const void* data, GLenum usage) { - (void)usage; - glBufferStorage(target, size, data, 0); + GLuint bound = 0; + + switch (target) + { + case GL_ARRAY_BUFFER: + bound = g_gl.boundArrayBuffer; + break; + + case GL_ELEMENT_ARRAY_BUFFER: + bound = g_gl.boundElementArrayBuffer; + break; + + default: + g_gl.lastError = GL_INVALID_ENUM; + return; + } + + if (size < 0) + { + g_gl.lastError = GL_INVALID_VALUE; + return; + } + + if (bound == 0) + { + g_gl.lastError = GL_INVALID_OPERATION; + return; + } + + GLBufferObject* bo = QD3D12_GetBuffer(bound); + if (!bo) + { + g_gl.lastError = GL_INVALID_OPERATION; + return; + } + + QD3D12_UpdateBufferResource(*bo, target, size, data, usage); } void APIENTRY glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const void* data) @@ -16640,6 +17278,11 @@ void APIENTRY glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, c } memcpy(bo->data.data() + (size_t)offset, data, (size_t)size); + ++bo->revision; + QD3D12_ResetPackedVertexBuffer(*bo); + + if (bo->mappedGpu) + memcpy(bo->mappedGpu + (size_t)offset, data, (size_t)size); } void APIENTRY glDrawArrays(GLenum mode, GLint first, GLsizei count) @@ -16771,6 +17414,13 @@ GLboolean APIENTRY glUnmapBuffer(GLenum target) } bo->mapped = false; + if (bo->mappedAccess & GL_MAP_WRITE_BIT) + { + ++bo->revision; + QD3D12_ResetPackedVertexBuffer(*bo); + if (bo->mappedGpu && bo->mappedLength > 0) + memcpy(bo->mappedGpu + (size_t)bo->mappedOffset, bo->data.data() + (size_t)bo->mappedOffset, (size_t)bo->mappedLength); + } bo->mappedOffset = 0; bo->mappedLength = 0; bo->mappedAccess = 0; diff --git a/neo/engine/opengl/opengl.h b/neo/engine/opengl/opengl.h index 736ded63..72b6963e 100644 --- a/neo/engine/opengl/opengl.h +++ b/neo/engine/opengl/opengl.h @@ -2328,6 +2328,7 @@ void QD3D12_SetUpscalerSharpness(float sharpness); void QD3D12_SetToneMapBrightness(float brightness); void QD3D12_SetFrameGenerationMultiplier(int multiplier); int QD3D12_GetFrameGenerationMultiplier(void); +void QD3D12_SetCurrentWindowDLSSAllowed(int allowed); void QD3D12_EnableTAA(int enabled); diff --git a/neo/engine/opengl/opengl.vcxproj b/neo/engine/opengl/opengl.vcxproj index 638404ec..ff9f9c1e 100644 --- a/neo/engine/opengl/opengl.vcxproj +++ b/neo/engine/opengl/opengl.vcxproj @@ -45,7 +45,23 @@ + + + USE_D3D=0;USE_AVX512=0;%(PreprocessorDefinitions) + MaxSpeed + MaxSpeed + MaxSpeed + Default + + + USE_D3D=0;USE_AVX512=0;%(PreprocessorDefinitions) + /arch:AVX2 %(AdditionalOptions) + MaxSpeed + MaxSpeed + MaxSpeed + Default + MaxSpeed MaxSpeed @@ -182,7 +198,11 @@ + + + + @@ -373,6 +393,8 @@ MultiThreadedDebug streamline/include;d3d12/; 4996 + Full + Default diff --git a/neo/engine/opengl/opengl.vcxproj.filters b/neo/engine/opengl/opengl.vcxproj.filters index 0a33b859..a4010f43 100644 --- a/neo/engine/opengl/opengl.vcxproj.filters +++ b/neo/engine/opengl/opengl.vcxproj.filters @@ -44,6 +44,13 @@ + + thirdparty\MaskedOcclusionCulling + + + thirdparty\MaskedOcclusionCulling + + @@ -98,6 +105,16 @@ + + thirdparty\MaskedOcclusionCulling + + + thirdparty\MaskedOcclusionCulling + + + thirdparty\MaskedOcclusionCulling + + @@ -106,6 +123,12 @@ {ac325e42-ffb4-4bbc-a4ec-b567658773a3} + + {8e15d873-a9f6-421d-97b5-3bb07f870144} + + + {9420a408-8b6e-4ce4-bd61-97537c296ec6} + @@ -117,4 +140,4 @@ streamline - \ No newline at end of file + diff --git a/neo/engine/renderer/RenderSystem.cpp b/neo/engine/renderer/RenderSystem.cpp index 823b50d6..7fa5749c 100644 --- a/neo/engine/renderer/RenderSystem.cpp +++ b/neo/engine/renderer/RenderSystem.cpp @@ -726,7 +726,7 @@ void idRenderSystemLocal::EndFrame( int *frontEndMsec, int *backEndMsec, bool sw *frontEndMsec = pc.frontEndMsec; } if ( backEndMsec ) { - *backEndMsec = backEnd.pc.msec; + *backEndMsec = backEnd.pc.cpuMsecNoSwap; } // print any other statistics and clear all of them diff --git a/neo/engine/renderer/RenderSystem_init.cpp b/neo/engine/renderer/RenderSystem_init.cpp index 8d43c31e..b03aa7d4 100644 --- a/neo/engine/renderer/RenderSystem_init.cpp +++ b/neo/engine/renderer/RenderSystem_init.cpp @@ -118,6 +118,8 @@ idCVar r_ignore2( "r_ignore2", "0", CVAR_RENDERER, "used for random debugging wi idCVar r_usePreciseTriangleInteractions( "r_usePreciseTriangleInteractions", "0", CVAR_RENDERER | CVAR_BOOL, "1 = do winding clipping to determine if each ambiguous tri should be lit" ); idCVar r_useCulling( "r_useCulling", "2", CVAR_RENDERER | CVAR_INTEGER, "0 = none, 1 = sphere, 2 = sphere + box", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> ); idCVar r_useLightCulling( "r_useLightCulling", "3", CVAR_RENDERER | CVAR_INTEGER, "0 = none, 1 = box, 2 = exact clip of polyhedron faces, 3 = also areas", 0, 3, idCmdSystem::ArgCompletion_Integer<0,3> ); +idCVar r_useMaskedOcclusionCulling( "r_useMaskedOcclusionCulling", "1", CVAR_RENDERER | CVAR_BOOL, "threaded one-frame-late masked occlusion culling for renderer lights" ); +idCVar r_mocResolutionScale( "r_mocResolutionScale", "0.5", CVAR_RENDERER | CVAR_FLOAT, "software occlusion buffer resolution scale for masked light culling", 0.125f, 1.0f ); idCVar r_useLightScissors( "r_useLightScissors", "1", CVAR_RENDERER | CVAR_BOOL, "1 = use custom scissor rectangle for each light" ); idCVar r_useClippedLightScissors( "r_useClippedLightScissors", "1", CVAR_RENDERER | CVAR_INTEGER, "0 = full screen when near clipped, 1 = exact when near clipped, 2 = exact always", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> ); idCVar r_useEntityCulling( "r_useEntityCulling", "1", CVAR_RENDERER | CVAR_BOOL, "0 = none, 1 = box" ); @@ -1988,6 +1990,8 @@ idRenderSystemLocal::Shutdown void idRenderSystemLocal::Shutdown( void ) { common->Printf( "idRenderSystem::Shutdown()\n" ); + R_MaskedOcclusionCulling_Shutdown(); + R_DoneFreeType( ); if ( glConfig.isInitialized ) { diff --git a/neo/engine/renderer/RenderWorld_load.cpp b/neo/engine/renderer/RenderWorld_load.cpp index b911b53d..fe07bc5f 100644 --- a/neo/engine/renderer/RenderWorld_load.cpp +++ b/neo/engine/renderer/RenderWorld_load.cpp @@ -269,23 +269,17 @@ idRenderModel *idRenderWorldLocal::ParseShadowModel( idLexer *src ) { tri->numIndexes = src->ParseInt(); tri->shadowCapPlaneBits = src->ParseInt(); - R_AllocStaticTriSurfShadowVerts( tri, tri->numVerts ); tri->bounds.Clear(); for ( j = 0 ; j < tri->numVerts ; j++ ) { float vec[8]; src->Parse1DMatrix( 3, vec ); - tri->shadowVertexes[j].xyz[0] = vec[0]; - tri->shadowVertexes[j].xyz[1] = vec[1]; - tri->shadowVertexes[j].xyz[2] = vec[2]; - tri->shadowVertexes[j].xyz[3] = 1; // no homogenous value - tri->bounds.AddPoint( tri->shadowVertexes[j].xyz.ToVec3() ); + tri->bounds.AddPoint( idVec3( vec[0], vec[1], vec[2] ) ); } - R_AllocStaticTriSurfIndexes( tri, tri->numIndexes ); for ( j = 0 ; j < tri->numIndexes ; j++ ) { - tri->indexes[j] = src->ParseInt(); + src->ParseInt(); } // add the completed surface to the model diff --git a/neo/engine/renderer/draw_common.cpp b/neo/engine/renderer/draw_common.cpp index 0fff0cef..58671212 100644 --- a/neo/engine/renderer/draw_common.cpp +++ b/neo/engine/renderer/draw_common.cpp @@ -32,6 +32,14 @@ If you have questions concerning this license or the applicable additional terms idCVar r_renderNeuralMaterial("r_renderNeuralMaterial", "0", CVAR_BOOL, ""); +static idDrawVert *RB_BindAmbientBuffer( const srfTriangles_t *tri ) { + if ( tri->ambientVbo ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, tri->ambientVbo ); + return (idDrawVert *)0; + } + return (idDrawVert *)vertexCache.Position( tri->ambientCache ); +} + /* ===================== RB_BakeTextureMatrixIntoTexgen @@ -389,7 +397,7 @@ void RB_T_FillDepthBuffer(const drawSurf_t* surf) { return; } - if (!tri->ambientCache) { + if (!tri->ambientVbo && !tri->ambientCache) { common->Printf("RB_T_FillDepthBuffer: !tri->ambientCache\n"); return; } @@ -435,7 +443,7 @@ void RB_T_FillDepthBuffer(const drawSurf_t* surf) { color[3] = 1.0f; } - idDrawVert* ac = (idDrawVert*)vertexCache.Position(tri->ambientCache); + idDrawVert* ac = RB_BindAmbientBuffer(tri); glVertexPointer(3, GL_FLOAT, sizeof(idDrawVert), ac->xyz.ToFloatPtr()); glTexCoordPointer(2, GL_FLOAT, sizeof(idDrawVert), reinterpret_cast(&ac->st)); @@ -558,7 +566,7 @@ void RB_T_FillDepthBuffer(const drawSurf_t* surf) { // draw it RB_DrawElementsWithCounters(tri); - RB_FinishStageTexturing(pStage, surf, ac); + //RB_FinishStageTexturing(pStage, surf, ac); if (glowMapImage) { GL_SelectTexture(3); @@ -642,11 +650,7 @@ void RB_STD_FillDepthBuffer( drawSurf_t **drawSurfs, int numDrawSurfs ) { GL_State( GLS_DEPTHFUNC_LESS ); - // Enable stencil test if we are going to be using it for shadows. - // If we didn't do this, it would be legal behavior to get z fighting - // from the ambient pass and the light passes. - glEnable( GL_STENCIL_TEST ); - glStencilFunc( GL_ALWAYS, 1, 255 ); + glDisable( GL_STENCIL_TEST ); RB_RenderDrawSurfListWithFunction( drawSurfs, numDrawSurfs, RB_T_FillDepthBuffer ); @@ -827,7 +831,7 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) { return; } - if ( !tri->ambientCache ) { + if ( !tri->ambientVbo && !tri->ambientCache ) { common->Printf( "RB_T_RenderShaderPasses: !tri->ambientCache\n" ); return; } @@ -852,7 +856,7 @@ void RB_STD_T_RenderShaderPasses( const drawSurf_t *surf ) { RB_EnterModelDepthHack( surf->space->modelDepthHack ); } - idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache ); + idDrawVert *ac = RB_BindAmbientBuffer( tri ); glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() ); glTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), reinterpret_cast(&ac->st) ); @@ -1167,13 +1171,9 @@ static void RB_T_BlendLight( const drawSurf_t *surf ) { glTexGenfv( GL_S, GL_OBJECT_PLANE, lightProject[3].ToFloatPtr() ); } - // this gets used for both blend lights and shadow draws - if ( tri->ambientCache ) { - idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache ); + if ( tri->ambientVbo || tri->ambientCache ) { + idDrawVert *ac = RB_BindAmbientBuffer( tri ); glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() ); - } else if ( tri->shadowCache ) { - shadowCache_t *sc = (shadowCache_t *)vertexCache.Position( tri->shadowCache ); - glVertexPointer( 3, GL_FLOAT, sizeof( shadowCache_t ), sc->xyz.ToFloatPtr() ); } RB_DrawElementsWithCounters( tri ); @@ -1323,7 +1323,7 @@ static void RB_FogPass( const drawSurf_t *drawSurfs, const drawSurf_t *drawSurf frustumTris = backEnd.vLight->frustumTris; // if we ran out of vertex cache memory, skip it - if ( !frustumTris->ambientCache ) { + if ( !frustumTris->ambientVbo && !frustumTris->ambientCache ) { return; } memset( &ds, 0, sizeof( ds ) ); @@ -1513,9 +1513,6 @@ void RB_STD_DrawView( void ) { // main light renderer RB_DXDrawInteractions(); - // disable stencil shadow test - glStencilFunc( GL_ALWAYS, 128, 255 ); - // now draw any non-light dependent shading passes int processed = RB_STD_DrawShaderPasses( drawSurfs, numDrawSurfs ); diff --git a/neo/engine/renderer/draw_dx.cpp b/neo/engine/renderer/draw_dx.cpp index 126b3f0e..fd6ccf27 100644 --- a/neo/engine/renderer/draw_dx.cpp +++ b/neo/engine/renderer/draw_dx.cpp @@ -174,6 +174,10 @@ void RB_DXDrawInteractions(void) { continue; } + if (!R_MaskedOcclusionCulling_LightIsVisible(backEnd.viewDef, vLight->lightDef)) + { + continue; + } const renderLight_t& srcLight = vLight->lightDef->parms; const float r = srcLight.shaderParms[SHADERPARM_RED]; diff --git a/neo/engine/renderer/tr_backend.cpp b/neo/engine/renderer/tr_backend.cpp index 0b39e8bc..c31d58f7 100644 --- a/neo/engine/renderer/tr_backend.cpp +++ b/neo/engine/renderer/tr_backend.cpp @@ -626,6 +626,7 @@ void RB_ExecuteBackEndCommands( const emptyCommand_t *cmds ) { } backEndStartTime = Sys_Milliseconds(); + int backendCpuNoSwap = 0; // needed for editor rendering RB_SetDefaultGLState(); @@ -634,7 +635,9 @@ void RB_ExecuteBackEndCommands( const emptyCommand_t *cmds ) { globalImages->CompleteBackgroundImageLoads(); for ( ; cmds ; cmds = (const emptyCommand_t *)cmds->next ) { - switch ( cmds->commandId ) { + const int commandStartTime = Sys_Milliseconds(); + const renderCommand_t commandId = cmds->commandId; + switch ( commandId ) { case RC_NOP: break; case RC_DRAW_VIEW: @@ -662,6 +665,9 @@ void RB_ExecuteBackEndCommands( const emptyCommand_t *cmds ) { common->Error( "RB_ExecuteBackEndCommands: bad commandId" ); break; } + if ( commandId != RC_SWAP_BUFFERS ) { + backendCpuNoSwap += Sys_Milliseconds() - commandStartTime; + } } // go back to the default texture so the editor doesn't mess up a bound image @@ -671,6 +677,7 @@ void RB_ExecuteBackEndCommands( const emptyCommand_t *cmds ) { // stop rendering on this thread backEndFinishTime = Sys_Milliseconds(); backEnd.pc.msec = backEndFinishTime - backEndStartTime; + backEnd.pc.cpuMsecNoSwap = backendCpuNoSwap; if ( r_debugRenderToTexture.GetInteger() == 1 ) { common->Printf( "3d: %i, 2d: %i, SetBuf: %i, SwpBuf: %i, CpyRenders: %i, CpyFrameBuf: %i\n", c_draw3d, c_draw2d, c_setBuffers, c_swapBuffers, c_copyRenders, backEnd.c_copyFrameBuffer ); diff --git a/neo/engine/renderer/tr_light.cpp b/neo/engine/renderer/tr_light.cpp index 3ff301a0..bc363c37 100644 --- a/neo/engine/renderer/tr_light.cpp +++ b/neo/engine/renderer/tr_light.cpp @@ -50,8 +50,8 @@ R_CreateAmbientCache Create it if needed ================== */ -bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting ) { - if ( tri->ambientCache ) { +bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting, bool staticCache ) { + if ( tri->ambientVbo || tri->ambientCache ) { return true; } // we are going to use it for drawing, so make sure we have the tangents and normals @@ -59,10 +59,40 @@ bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting ) { R_DeriveTangents( tri ); } - vertexCache.Alloc( tri->verts, tri->numVerts * sizeof( tri->verts[0] ), &tri->ambientCache ); - if ( !tri->ambientCache ) { + if ( !tri->verts || tri->numVerts <= 0 ) { return false; } + + if ( staticCache ) { + glGenBuffersARB( 1, &tri->ambientVbo ); + glBindBufferARB( GL_ARRAY_BUFFER_ARB, tri->ambientVbo ); + glBufferDataARB( GL_ARRAY_BUFFER_ARB, (GLsizeiptrARB)( tri->numVerts * sizeof( tri->verts[0] ) ), tri->verts, GL_STATIC_DRAW_ARB ); + return true; + } + + vertexCache.Alloc( tri->verts, tri->numVerts * sizeof( tri->verts[0] ), &tri->ambientCache ); + return tri->ambientCache != NULL; +} + +/* +================== +R_CreateIndexCache + +Static surfaces own their index buffer directly instead of routing through +idVertexCache. +================== +*/ +bool R_CreateIndexCache( srfTriangles_t *tri ) { + if ( tri->indexVbo ) { + return true; + } + if ( !tri->indexes || tri->numIndexes <= 0 ) { + return false; + } + + glGenBuffersARB( 1, &tri->indexVbo ); + glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, tri->indexVbo ); + glBufferDataARB( GL_ELEMENT_ARRAY_BUFFER_ARB, (GLsizeiptrARB)( tri->numIndexes * sizeof( tri->indexes[0] ) ), tri->indexes, GL_STATIC_DRAW_ARB ); return true; } @@ -139,11 +169,7 @@ This is used only for a specific light ================== */ void R_CreatePrivateShadowCache( srfTriangles_t *tri ) { - if ( !tri->shadowVertexes ) { - return; - } - - vertexCache.Alloc( tri->shadowVertexes, tri->numVerts * sizeof( *tri->shadowVertexes ), &tri->shadowCache ); + (void)tri; } /* @@ -155,35 +181,7 @@ takes care of projecting the verts to infinity. ================== */ void R_CreateVertexProgramShadowCache( srfTriangles_t *tri ) { - if ( tri->verts == NULL ) { - return; - } - - shadowCache_t *temp = (shadowCache_t *)_alloca16( tri->numVerts * 2 * sizeof( shadowCache_t ) ); - -#if 1 - - SIMDProcessor->CreateVertexProgramShadowCache( &temp->xyz, tri->verts, tri->numVerts ); - -#else - - int numVerts = tri->numVerts; - const idDrawVert *verts = tri->verts; - for ( int i = 0; i < numVerts; i++ ) { - const float *v = verts[i].xyz.ToFloatPtr(); - temp[i*2+0].xyz[0] = v[0]; - temp[i*2+1].xyz[0] = v[0]; - temp[i*2+0].xyz[1] = v[1]; - temp[i*2+1].xyz[1] = v[1]; - temp[i*2+0].xyz[2] = v[2]; - temp[i*2+1].xyz[2] = v[2]; - temp[i*2+0].xyz[3] = 1.0f; // on the model surface - temp[i*2+1].xyz[3] = 0.0f; // will be projected to infinity - } - -#endif - - vertexCache.Alloc( temp, tri->numVerts * 2 * sizeof( shadowCache_t ), &tri->shadowCache ); + (void)tri; } /* @@ -956,55 +954,18 @@ void R_AddLightSurfaces( void ) { // fog lights will need to draw the light frustum triangles, so make sure they // are in the vertex cache if ( lightShader->IsFogLight() ) { - if ( !light->frustumTris->ambientCache ) { + if ( !light->frustumTris->ambientVbo ) { if ( !R_CreateAmbientCache( light->frustumTris, false ) ) { // skip if we are out of vertex memory continue; } } - // touch the surface so it won't get purged - vertexCache.Touch( light->frustumTris->ambientCache ); + if ( r_useIndexBuffers.GetBool() ) { + R_CreateIndexCache( light->frustumTris ); + } } - // add the prelight shadows for the static world geometry - if ( light->parms.prelightModel && r_useOptimizedShadows.GetBool() ) { - - if ( !light->parms.prelightModel->NumSurfaces() ) { - common->Error( "no surfs in prelight model '%s'", light->parms.prelightModel->Name() ); - } - - srfTriangles_t *tri = light->parms.prelightModel->Surface( 0 )->geometry; - if ( !tri->shadowVertexes ) { - common->Error( "R_AddLightSurfaces: prelight model '%s' without shadowVertexes", light->parms.prelightModel->Name() ); - } - - // these shadows will all have valid bounds, and can be culled normally - if ( r_useShadowCulling.GetBool() ) { - if ( R_CullLocalBox( tri->bounds, tr.viewDef->worldSpace.modelMatrix, 5, tr.viewDef->frustum ) ) { - continue; - } - } - - // if we have been purged, re-upload the shadowVertexes - if ( !tri->shadowCache ) { - R_CreatePrivateShadowCache( tri ); - if ( !tri->shadowCache ) { - continue; - } - } - - // touch the shadow surface so it won't get purged - vertexCache.Touch( tri->shadowCache ); - - if ( !tri->indexCache && r_useIndexBuffers.GetBool() ) { - vertexCache.Alloc( tri->indexes, tri->numIndexes * sizeof( tri->indexes[0] ), &tri->indexCache, true ); - } - if ( tri->indexCache ) { - vertexCache.Touch( tri->indexCache ); - } - - R_LinkLightSurf( &vLight->globalShadows, tri, NULL, light, NULL, vLight->scissorRect, true /* FIXME? */ ); - } + // Stencil/prelight shadow volumes are not used by the D3D12 lighting path. } } @@ -1379,18 +1340,14 @@ static void R_AddAmbientDrawsurfs( viewEntity_t *vEntity ) { def->visibleCount = tr.viewCount; // make sure we have an ambient cache - if ( !R_CreateAmbientCache( tri, shader->ReceivesLighting() ) ) { + const bool staticCache = def->dynamicModel == NULL; + const bool needsAmbientTangents = staticCache && shader->ReceivesLighting(); + if ( !R_CreateAmbientCache( tri, needsAmbientTangents, staticCache ) ) { // don't add anything if the vertex cache was too full to give us an ambient cache return; } - // touch it so it won't get purged - vertexCache.Touch( tri->ambientCache ); - - if ( r_useIndexBuffers.GetBool() && !tri->indexCache ) { - vertexCache.Alloc( tri->indexes, tri->numIndexes * sizeof( tri->indexes[0] ), &tri->indexCache, true ); - } - if ( tri->indexCache ) { - vertexCache.Touch( tri->indexCache ); + if ( staticCache && r_useIndexBuffers.GetBool() ) { + R_CreateIndexCache( tri ); } // add the surface for drawing @@ -1519,13 +1476,8 @@ void R_RemoveUnecessaryViewLights( void ) { // go through each visible light for ( vLight = tr.viewDef->viewLights ; vLight ; vLight = vLight->next ) { - // if the light didn't have any lit surfaces visible, there is no need to - // draw any of the shadows. We still keep the vLight for debugging - // draws - if ( !vLight->localInteractions && !vLight->globalInteractions && !vLight->translucentInteractions ) { - vLight->localShadows = NULL; - vLight->globalShadows = NULL; - } + vLight->localShadows = NULL; + vLight->globalShadows = NULL; } if ( r_useShadowSurfaceScissor.GetBool() ) { @@ -1545,16 +1497,9 @@ void R_RemoveUnecessaryViewLights( void ) { for ( surf = vLight->globalInteractions ; surf ; surf = surf->nextOnLight ) { surfRect.Union( surf->scissorRect ); } - for ( surf = vLight->localShadows ; surf ; surf = surf->nextOnLight ) { - const_cast(surf)->scissorRect.Intersect( surfRect ); - } - for ( surf = vLight->localInteractions ; surf ; surf = surf->nextOnLight ) { surfRect.Union( surf->scissorRect ); } - for ( surf = vLight->globalShadows ; surf ; surf = surf->nextOnLight ) { - const_cast(surf)->scissorRect.Intersect( surfRect ); - } for ( surf = vLight->translucentInteractions ; surf ; surf = surf->nextOnLight ) { surfRect.Union( surf->scissorRect ); diff --git a/neo/engine/renderer/tr_local.h b/neo/engine/renderer/tr_local.h index e0f22ee3..a9b72211 100644 --- a/neo/engine/renderer/tr_local.h +++ b/neo/engine/renderer/tr_local.h @@ -628,6 +628,7 @@ typedef struct { float maxLightValue; // for light scale int msec; // total msec for backend run + int cpuMsecNoSwap; // backend CPU excluding swap/present wait } backEndCounters_t; // all state modified by the back end is separated @@ -860,6 +861,8 @@ extern idCVar r_useNodeCommonChildren; // stop pushing reference bounds early wh extern idCVar r_useSilRemap; // 1 = consider verts with the same XYZ, but different ST the same for shadows extern idCVar r_useCulling; // 0 = none, 1 = sphere, 2 = sphere + box extern idCVar r_useLightCulling; // 0 = none, 1 = box, 2 = exact clip of polyhedron faces +extern idCVar r_useMaskedOcclusionCulling; // one-frame-late threaded MOC light culling +extern idCVar r_mocResolutionScale; // software occlusion resolution relative to view size extern idCVar r_useLightScissors; // 1 = use custom scissor rectangle for each light extern idCVar r_useClippedLightScissors;// 0 = full screen when near clipped, 1 = exact when near clipped, 2 = exact always extern idCVar r_useEntityCulling; // 0 = none, 1 = box @@ -1142,6 +1145,9 @@ MAIN */ void R_RenderView( viewDef_t *parms ); +void R_MaskedOcclusionCulling_ProcessView( viewDef_t *parms ); +bool R_MaskedOcclusionCulling_LightIsVisible( const viewDef_t *viewDef, const idRenderLightLocal *light ); +void R_MaskedOcclusionCulling_Shutdown( void ); // performs radius cull first, then corner cull bool R_CullLocalBox( const idBounds &bounds, const float modelMatrix[16], int numPlanes, const idPlane *planes ); @@ -1195,7 +1201,8 @@ void R_AddDrawSurf( const srfTriangles_t *tri, const viewEntity_t *space, const void R_LinkLightSurf( const drawSurf_t **link, const srfTriangles_t *tri, const viewEntity_t *space, const idRenderLightLocal *light, const idMaterial *shader, const idScreenRect &scissor, bool viewInsideShadow ); -bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting ); +bool R_CreateAmbientCache( srfTriangles_t *tri, bool needsLighting, bool staticCache = true ); +bool R_CreateIndexCache( srfTriangles_t *tri ); bool R_CreateLightingCache( const idRenderEntityLocal *ent, const idRenderLightLocal *light, srfTriangles_t *tri ); void R_CreatePrivateShadowCache( srfTriangles_t *tri ); void R_CreateVertexProgramShadowCache( srfTriangles_t *tri ); diff --git a/neo/engine/renderer/tr_main.cpp b/neo/engine/renderer/tr_main.cpp index 5f5d56fe..c018f57b 100644 --- a/neo/engine/renderer/tr_main.cpp +++ b/neo/engine/renderer/tr_main.cpp @@ -1123,6 +1123,10 @@ void R_RenderView( viewDef_t *parms ) { // lightDefs that are in them and pass culling. static_cast(parms->renderWorld)->FindViewLightsAndEntities(); + // Queue this full light list for next frame, then prune with the last + // completed async masked occlusion result. + R_MaskedOcclusionCulling_ProcessView( tr.viewDef ); + // constrain the view frustum to the view lights and entities R_ConstrainViewFrustum(); diff --git a/neo/engine/renderer/tr_render.cpp b/neo/engine/renderer/tr_render.cpp index 616f9094..3d6880f3 100644 --- a/neo/engine/renderer/tr_render.cpp +++ b/neo/engine/renderer/tr_render.cpp @@ -69,6 +69,14 @@ void RB_DrawElementsImmediate( const srfTriangles_t *tri ) { glEnd(); } +static idDrawVert *RB_BindAmbientBuffer( const srfTriangles_t *tri ) { + if ( tri->ambientVbo ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, tri->ambientVbo ); + return (idDrawVert *)0; + } + return (idDrawVert *)vertexCache.Position( tri->ambientCache ); +} + /* ================ @@ -90,7 +98,14 @@ void RB_DrawElementsWithCounters( const srfTriangles_t *tri ) { } } - if ( tri->indexCache && r_useIndexBuffers.GetBool() ) { + if ( tri->indexVbo && r_useIndexBuffers.GetBool() ) { + glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, tri->indexVbo ); + glDrawElements( GL_TRIANGLES, + r_singleTriangle.GetBool() ? 3 : tri->numIndexes, + GL_INDEX_TYPE, + (void *)0 ); + backEnd.pc.c_vboIndexes += tri->numIndexes; + } else if ( tri->indexCache && r_useIndexBuffers.GetBool() ) { glDrawElements( GL_TRIANGLES, r_singleTriangle.GetBool() ? 3 : tri->numIndexes, GL_INDEX_TYPE, @@ -119,7 +134,14 @@ void RB_DrawShadowElementsWithCounters( const srfTriangles_t *tri, int numIndexe backEnd.pc.c_shadowIndexes += numIndexes; backEnd.pc.c_shadowVertexes += tri->numVerts; - if ( tri->indexCache && r_useIndexBuffers.GetBool() ) { + if ( tri->indexVbo && r_useIndexBuffers.GetBool() ) { + glBindBufferARB( GL_ELEMENT_ARRAY_BUFFER_ARB, tri->indexVbo ); + glDrawElements( GL_TRIANGLES, + r_singleTriangle.GetBool() ? 3 : numIndexes, + GL_INDEX_TYPE, + (void *)0 ); + backEnd.pc.c_vboIndexes += numIndexes; + } else if ( tri->indexCache && r_useIndexBuffers.GetBool() ) { glDrawElements( GL_TRIANGLES, r_singleTriangle.GetBool() ? 3 : numIndexes, GL_INDEX_TYPE, @@ -145,13 +167,13 @@ Sets texcoord and vertex pointers =============== */ void RB_RenderTriangleSurface( const srfTriangles_t *tri ) { - if ( !tri->ambientCache ) { + if ( !tri->ambientVbo && !tri->ambientCache ) { RB_DrawElementsImmediate( tri ); return; } - idDrawVert *ac = (idDrawVert *)vertexCache.Position( tri->ambientCache ); + idDrawVert *ac = RB_BindAmbientBuffer( tri ); glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ac->xyz.ToFloatPtr() ); glTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), ac->st.ToFloatPtr() ); @@ -413,7 +435,7 @@ void RB_BindStageTexture( const float *shaderRegisters, const textureStage_t *te // texgens if ( texture->texgen == TG_DIFFUSE_CUBE ) { - glTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), ((idDrawVert *)vertexCache.Position( surf->geo->ambientCache ))->normal.ToFloatPtr() ); + glTexCoordPointer( 3, GL_FLOAT, sizeof( idDrawVert ), RB_BindAmbientBuffer( surf->geo )->normal.ToFloatPtr() ); } if ( texture->texgen == TG_SKYBOX_CUBE || texture->texgen == TG_WOBBLESKY_CUBE ) { glTexCoordPointer( 3, GL_FLOAT, 0, vertexCache.Position( surf->dynamicTexCoords ) ); @@ -426,7 +448,7 @@ void RB_BindStageTexture( const float *shaderRegisters, const textureStage_t *te glTexGenf( GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT ); glTexGenf( GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_EXT ); glEnableClientState( GL_NORMAL_ARRAY ); - glNormalPointer( GL_FLOAT, sizeof( idDrawVert ), ((idDrawVert *)vertexCache.Position( surf->geo->ambientCache ))->normal.ToFloatPtr() ); + glNormalPointer( GL_FLOAT, sizeof( idDrawVert ), RB_BindAmbientBuffer( surf->geo )->normal.ToFloatPtr() ); glMatrixMode( GL_TEXTURE ); float mat[16]; @@ -452,7 +474,7 @@ void RB_FinishStageTexture( const textureStage_t *texture, const drawSurf_t *sur if ( texture->texgen == TG_DIFFUSE_CUBE || texture->texgen == TG_SKYBOX_CUBE || texture->texgen == TG_WOBBLESKY_CUBE ) { glTexCoordPointer( 2, GL_FLOAT, sizeof( idDrawVert ), - (void *)&(((idDrawVert *)vertexCache.Position( surf->geo->ambientCache ))->st) ); + (void *)&(RB_BindAmbientBuffer( surf->geo )->st) ); } if ( texture->texgen == TG_REFLECT_CUBE ) { @@ -610,12 +632,9 @@ void RB_BeginDrawingView (void) { // we don't have to clear the depth / stencil buffer for 2D rendering if ( backEnd.viewDef->viewEntitys ) { - glStencilMask( 0xff ); - // some cards may have 7 bit stencil buffers, so don't assume this - // should be 128 - glClearStencil( 1<<(glConfig.stencilBits-1) ); - glClear( GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); + glClear( GL_DEPTH_BUFFER_BIT ); glEnable( GL_DEPTH_TEST ); + glDisable( GL_STENCIL_TEST ); } else { glDisable( GL_DEPTH_TEST ); glDisable( GL_STENCIL_TEST ); @@ -728,7 +747,7 @@ void RB_CreateSingleDrawInteractions( const drawSurf_t *surf, void (*DrawInterac const float *lightRegs = vLight->shaderRegisters; drawInteraction_t inter; - if ( r_skipInteractions.GetBool() || !surf->geo || !surf->geo->ambientCache ) { + if ( r_skipInteractions.GetBool() || !surf->geo || ( !surf->geo->ambientVbo && !surf->geo->ambientCache ) ) { return; } diff --git a/neo/engine/renderer/tr_rendertools.cpp b/neo/engine/renderer/tr_rendertools.cpp index 80371bcd..1d4b254b 100644 --- a/neo/engine/renderer/tr_rendertools.cpp +++ b/neo/engine/renderer/tr_rendertools.cpp @@ -474,6 +474,14 @@ void RB_ShowDepthBuffer( void ) { R_StaticFree( depthReadback ); } +static idDrawVert *RB_BindToolAmbientBuffer( const srfTriangles_t *tri ) { + if ( tri->ambientVbo ) { + glBindBufferARB( GL_ARRAY_BUFFER_ARB, tri->ambientVbo ); + return (idDrawVert *)0; + } + return (idDrawVert *)vertexCache.Position( tri->ambientCache ); +} + /* ================= RB_ShowLightCount @@ -514,11 +522,11 @@ void RB_ShowLightCount( void ) { for ( i = 0 ; i < 2 ; i++ ) { for ( surf = i ? vLight->localInteractions: vLight->globalInteractions; surf; surf = (drawSurf_t *)surf->nextOnLight ) { RB_SimpleSurfaceSetup( surf ); - if ( !surf->geo->ambientCache ) { + if ( !surf->geo->ambientVbo && !surf->geo->ambientCache ) { continue; } - const idDrawVert *ac = (idDrawVert *)vertexCache.Position( surf->geo->ambientCache ); + const idDrawVert *ac = RB_BindToolAmbientBuffer( surf->geo ); glVertexPointer( 3, GL_FLOAT, sizeof( idDrawVert ), &ac->xyz ); RB_DrawElementsWithCounters( surf->geo ); } @@ -543,77 +551,7 @@ plane extends from, allowing you to see doubled edges ================= */ void RB_ShowSilhouette( void ) { - int i; - const drawSurf_t *surf; - const viewLight_t *vLight; - - if ( !r_showSilhouette.GetBool() ) { - return; - } - - // - // clear all triangle edges to black - // - glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - globalImages->BindNull(); - glDisable( GL_TEXTURE_2D ); - glDisable( GL_STENCIL_TEST ); - - glColor3f( 0, 0, 0 ); - - GL_State( GLS_POLYMODE_LINE ); - - GL_Cull( CT_TWO_SIDED ); - glDisable( GL_DEPTH_TEST ); - - RB_RenderDrawSurfListWithFunction( backEnd.viewDef->drawSurfs, backEnd.viewDef->numDrawSurfs, - RB_T_RenderTriangleSurface ); - - - // - // now blend in edges that cast silhouettes - // - RB_SimpleWorldSetup(); - glColor3f( 0.5, 0, 0 ); - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - - for ( vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) { - for ( i = 0 ; i < 2 ; i++ ) { - for ( surf = i ? vLight->localShadows : vLight->globalShadows - ; surf ; surf = (drawSurf_t *)surf->nextOnLight ) { - RB_SimpleSurfaceSetup( surf ); - - const srfTriangles_t *tri = surf->geo; - - glVertexPointer( 3, GL_FLOAT, sizeof( shadowCache_t ), vertexCache.Position( tri->shadowCache ) ); - glBegin( GL_LINES ); - - for ( int j = 0 ; j < tri->numIndexes ; j+=3 ) { - int i1 = tri->indexes[j+0]; - int i2 = tri->indexes[j+1]; - int i3 = tri->indexes[j+2]; - - if ( (i1 & 1) + (i2 & 1) + (i3 & 1) == 1 ) { - if ( (i1 & 1) + (i2 & 1) == 0 ) { - glArrayElement( i1 ); - glArrayElement( i2 ); - } else if ( (i1 & 1 ) + (i3 & 1) == 0 ) { - glArrayElement( i1 ); - glArrayElement( i3 ); - } - } - } - glEnd(); - - } - } - } - - glEnable( GL_DEPTH_TEST ); - - GL_State( GLS_DEFAULT ); - glColor3f( 1,1,1 ); - GL_Cull( CT_FRONT_SIDED ); + return; } @@ -627,76 +565,7 @@ and count up the total fill usage ================= */ static void RB_ShowShadowCount( void ) { - int i; - const drawSurf_t *surf; - const viewLight_t *vLight; - - if ( !r_showShadowCount.GetBool() ) { - return; - } - - GL_State( GLS_DEFAULT ); - - glClearStencil( 0 ); - glClear( GL_STENCIL_BUFFER_BIT ); - - glEnable( GL_STENCIL_TEST ); - - glStencilOp( GL_KEEP, GL_INCR, GL_INCR ); - - glStencilFunc( GL_ALWAYS, 1, 255 ); - - globalImages->defaultImage->Bind(); - - // draw both sides - GL_Cull( CT_TWO_SIDED ); - - for ( vLight = backEnd.viewDef->viewLights ; vLight ; vLight = vLight->next ) { - for ( i = 0 ; i < 2 ; i++ ) { - for ( surf = i ? vLight->localShadows : vLight->globalShadows - ; surf ; surf = (drawSurf_t *)surf->nextOnLight ) { - RB_SimpleSurfaceSetup( surf ); - const srfTriangles_t *tri = surf->geo; - if ( !tri->shadowCache ) { - continue; - } - - if ( r_showShadowCount.GetInteger() == 3 ) { - // only show turboshadows - if ( tri->numShadowIndexesNoCaps != tri->numIndexes ) { - continue; - } - } - if ( r_showShadowCount.GetInteger() == 4 ) { - // only show static shadows - if ( tri->numShadowIndexesNoCaps == tri->numIndexes ) { - continue; - } - } - - shadowCache_t *cache = (shadowCache_t *)vertexCache.Position( tri->shadowCache ); - glVertexPointer( 4, GL_FLOAT, sizeof( *cache ), &cache->xyz ); - RB_DrawElementsWithCounters( tri ); - } - } - } - - // display the results - R_ColorByStencilBuffer(); - - if ( r_showShadowCount.GetInteger() == 2 ) { - common->Printf( "all shadows " ); - } else if ( r_showShadowCount.GetInteger() == 3 ) { - common->Printf( "turboShadows " ); - } else if ( r_showShadowCount.GetInteger() == 4 ) { - common->Printf( "static shadows " ); - } - - if ( r_showShadowCount.GetInteger() >= 2 ) { - RB_CountStencilBuffer(); - } - - GL_Cull( CT_FRONT_SIDED ); + return; } diff --git a/neo/engine/renderer/tr_trisurf.cpp b/neo/engine/renderer/tr_trisurf.cpp index d0a3b9eb..8d4116c9 100644 --- a/neo/engine/renderer/tr_trisurf.cpp +++ b/neo/engine/renderer/tr_trisurf.cpp @@ -340,6 +340,10 @@ void R_FreeStaticTriSurfVertexCaches( srfTriangles_t *tri ) { // this is a real model surface vertexCache.Free( tri->ambientCache ); tri->ambientCache = NULL; + if ( tri->ambientVbo ) { + glDeleteBuffersARB( 1, &tri->ambientVbo ); + tri->ambientVbo = 0; + } } else { // this is a light interaction surface that references // a different ambient model surface @@ -350,6 +354,10 @@ void R_FreeStaticTriSurfVertexCaches( srfTriangles_t *tri ) { vertexCache.Free( tri->indexCache ); tri->indexCache = NULL; } + if ( tri->indexVbo ) { + glDeleteBuffersARB( 1, &tri->indexVbo ); + tri->indexVbo = 0; + } if ( tri->shadowCache && ( tri->shadowVertexes != NULL || tri->verts != NULL ) ) { // if we don't have tri->shadowVertexes, these are a reference to a // shadowCache on the original surface, which a vertex program @@ -2272,4 +2280,3 @@ int R_DeformInfoMemoryUsed( deformInfo_t *deformInfo ) { total += sizeof( *deformInfo ); return total; } - diff --git a/neo/engine/tools/radiant/CamWnd.cpp b/neo/engine/tools/radiant/CamWnd.cpp index 4daef00f..e9c80abd 100644 --- a/neo/engine/tools/radiant/CamWnd.cpp +++ b/neo/engine/tools/radiant/CamWnd.cpp @@ -36,6 +36,7 @@ If you have questions concerning this license or the applicable additional terms #include "OutlinerDlg.h" #include "splines.h" #include +#include #include "../../renderer/tr_local.h" #include "../../models/model_local.h" // for idRenderModelMD5 @@ -1564,24 +1565,38 @@ static bool CamWnd_IsTriggerLikeBrushEntity(brush_t* brush) { CamWnd_StringStartsWithNoCase(className, "monsterclip"); } -static bool CamWnd_TargetLinesVisible(CCamWnd* cam) { - return (CamWnd_GetFilterMask(cam) & CAMWND_FILTER_HIDE_TARGET_LINES) == 0; +struct camWndBrushFilterContext_t { + CCamWnd* cam; + int mask; + bool hasHiddenEntities; +}; + +static camWndBrushFilterContext_t CamWnd_MakeBrushFilterContext(CCamWnd* cam) { + camWndBrushFilterContext_t ctx; + ctx.cam = cam; + ctx.mask = CamWnd_GetFilterMask(cam); + ctx.hasHiddenEntities = Outliner_HasHiddenEntities(); + return ctx; } -static bool CamWnd_MenuFilterBrush(CCamWnd* cam, brush_t* brush) { +static bool CamWnd_TargetLinesVisible(const camWndBrushFilterContext_t& filter) { + return (filter.mask & CAMWND_FILTER_HIDE_TARGET_LINES) == 0; +} + +static bool CamWnd_MenuFilterBrush(const camWndBrushFilterContext_t& filter, brush_t* brush) { if (!brush) { return false; } - if (brush->owner != NULL && !Outliner_IsEntityVisible(brush->owner)) { + if (filter.hasHiddenEntities && brush->owner != NULL && !Outliner_IsEntityVisible(brush->owner)) { return true; } - if (!cam) { + if (!filter.cam) { return false; } - const int mask = CamWnd_GetFilterMask(cam); + const int mask = filter.mask; if (mask == 0) { return false; } @@ -1618,6 +1633,11 @@ static bool CamWnd_MenuFilterBrush(CCamWnd* cam, brush_t* brush) { return false; } +static bool CamWnd_MenuFilterBrush(CCamWnd* cam, brush_t* brush) { + const camWndBrushFilterContext_t filter = CamWnd_MakeBrushFilterContext(cam); + return CamWnd_MenuFilterBrush(filter, brush); +} + /* ======================== @@ -3556,6 +3576,7 @@ void setGLMode(int mode) { break; case cd_texture: + case cd_light: glCullFace(GL_FRONT); glEnable(GL_CULL_FACE); glShadeModel(GL_FLAT); @@ -3577,6 +3598,215 @@ void setGLMode(int mode) { } } +struct camWndWorldFaceBatch_t { + const idMaterial* material; + std::vector faces; +}; + +static bool CamWnd_IsFastWorldBrush(brush_t* brush) { + if (brush == NULL || brush->hiddenBrush) { + return false; + } + if (brush->pPatch || brush->modelHandle > 0 || brush->entityModel || brush->forceWireFrame) { + return false; + } + if (brush->owner == NULL || brush->owner != world_entity || brush->owner->curve != NULL) { + return false; + } + if (brush->owner->eclass == NULL || !(brush->owner->eclass->nShowFlags & ECLASS_WORLDSPAWN)) { + return false; + } + return true; +} + +static bool CamWnd_CanFastDrawWorldMode(int drawMode) { + return drawMode == cd_solid || drawMode == cd_texture || drawMode == cd_light; +} + +static bool CamWnd_ShouldDrawFastFace(face_t* face) { + if (face == NULL || face->face_winding == NULL || face->face_winding->GetNumPoints() < 3) { + return false; + } + if ((g_qeglobals.d_savedinfo.exclude & EXCLUDE_CAULK) && strstr(face->texdef.name, "caulk")) { + return false; + } + if ((g_qeglobals.d_savedinfo.exclude & EXCLUDE_VISPORTALS) && strstr(face->texdef.name, "visportal")) { + return false; + } + if ((g_qeglobals.d_savedinfo.exclude & EXCLUDE_NODRAW) && strstr(face->texdef.name, "nodraw")) { + return false; + } + return true; +} + +static camWndWorldFaceBatch_t* CamWnd_FindWorldFaceBatch(std::vector& batches, const idMaterial* material) { + for (size_t i = 0; i < batches.size(); i++) { + if (batches[i].material == material) { + return &batches[i]; + } + } + + camWndWorldFaceBatch_t batch; + batch.material = material; + batch.faces.reserve(128); + batches.push_back(batch); + return &batches.back(); +} + +static void CamWnd_AddFastWorldBrush(std::vector& batches, brush_t* brush, int drawMode) { + const bool textured = (drawMode == cd_texture || drawMode == cd_light); + + for (face_t* face = brush->brush_faces; face; face = face->next) { + if (!CamWnd_ShouldDrawFastFace(face)) { + continue; + } + + const idMaterial* material = textured ? face->d_texture : NULL; + CamWnd_FindWorldFaceBatch(batches, material)->faces.push_back(face); + } +} + +static void CamWnd_EmitFastWorldFaceBatches(const std::vector& batches, int drawMode) { + const bool textured = (drawMode == cd_texture || drawMode == cd_light); + + for (size_t batchIndex = 0; batchIndex < batches.size(); batchIndex++) { + const camWndWorldFaceBatch_t& batch = batches[batchIndex]; + if (batch.faces.empty()) { + continue; + } + + if (textured && batch.material != NULL) { + batch.material->GetEditorImage()->Bind(); + } + else { + globalImages->BindNull(); + } + + glBegin(GL_TRIANGLES); + for (size_t faceIndex = 0; faceIndex < batch.faces.size(); faceIndex++) { + face_t* face = batch.faces[faceIndex]; + idWinding* w = face->face_winding; + const int numPoints = w->GetNumPoints(); + const float alpha = face->d_texture ? face->d_texture->GetEditorAlpha() : 1.0f; + + glColor4f(face->d_color.x, face->d_color.y, face->d_color.z, alpha); + + for (int i = 2; i < numPoints; i++) { + if (textured) { + glTexCoord2fv(&(*w)[0][3]); + } + glVertex3fv((*w)[0].ToFloatPtr()); + + if (textured) { + glTexCoord2fv(&(*w)[i - 1][3]); + } + glVertex3fv((*w)[i - 1].ToFloatPtr()); + + if (textured) { + glTexCoord2fv(&(*w)[i][3]); + } + glVertex3fv((*w)[i].ToFloatPtr()); + } + } + glEnd(); + } + + globalImages->BindNull(); +} + +static void CamWnd_DrawActiveBrushes(CCamWnd* cam, bool renderMode, bool entityMode, const camWndBrushFilterContext_t& filter) { + std::vector batches; + std::vector slowBrushes; + batches.reserve(64); + slowBrushes.reserve(64); + + for (brush_t* brush = active_brushes.next; brush != &active_brushes; brush = brush->next) { + if (cam->CullBrush(brush, false)) { + continue; + } + + if (FilterBrush(brush)) { + continue; + } + + if (CamWnd_MenuFilterBrush(filter, brush)) { + continue; + } + + if (renderMode) { + if (!(entityMode && brush->owner->eclass->fixedsize)) { + continue; + } + slowBrushes.push_back(brush); + continue; + } + + if (CamWnd_CanFastDrawWorldMode(cam->Camera().draw_mode) && CamWnd_IsFastWorldBrush(brush)) { + CamWnd_AddFastWorldBrush(batches, brush, cam->Camera().draw_mode); + } + else { + slowBrushes.push_back(brush); + } + } + + if (!batches.empty()) { + setGLMode(cam->Camera().draw_mode); + CamWnd_EmitFastWorldFaceBatches(batches, cam->Camera().draw_mode); + } + + for (size_t i = 0; i < slowBrushes.size(); i++) { + setGLMode(cam->Camera().draw_mode); + Brush_Draw(slowBrushes[i]); + } +} + +static void CamWnd_DrawRadiantLightFrustum(const renderLight_t& lightParms, const idVec3& color) { + idPlane planes[6]; + R_RenderLightFrustum(lightParms, planes); + srfTriangles_t* tri = R_PolytopeSurface(6, planes, NULL); + if (tri == NULL) { + return; + } + + glColor3fv(color.ToFloatPtr()); + for (int i = 0; i < tri->numIndexes; i += 3) { + glBegin(GL_LINE_LOOP); + glVertex3fv(tri->verts[tri->indexes[i]].xyz.ToFloatPtr()); + glVertex3fv(tri->verts[tri->indexes[i + 1]].xyz.ToFloatPtr()); + glVertex3fv(tri->verts[tri->indexes[i + 2]].xyz.ToFloatPtr()); + glEnd(); + } + + R_FreeStaticTriSurf(tri); +} + +static void CamWnd_DrawRadiantLightFrustums(CCamWnd* cam) { + if (cam == NULL || !cam->GetRenderMode() || !(cam->GetEntityMode() || g_bShowLightVolumes)) { + return; + } + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glLineWidth(1.0f); + globalImages->BindNull(); + + for (entity_t* ent = entities.next; ent != &entities; ent = ent->next) { + if (ent->eclass == NULL || !(ent->eclass->nShowFlags & ECLASS_LIGHT) || ent->epairs.GetBool("start_off")) { + continue; + } + + idDict spawnArgs = ent->epairs; + renderLight_t lightParms; + gameEdit->ParseSpawnArgsToRenderLight(&spawnArgs, &lightParms); + + CamWnd_DrawRadiantLightFrustum(lightParms, idVec3(1.0f, 0.0f, 1.0f)); + } + + glEnable(GL_DEPTH_TEST); +} + static bool CamWnd_FaceHasAllPlanePointsSelected(face_t* face) { if (face == NULL || face->face_winding == NULL) { return false; @@ -3590,13 +3820,13 @@ static bool CamWnd_FaceHasAllPlanePointsSelected(face_t* face) { return true; } -static void CamWnd_DrawMoveSelectedBrushSidesForList(CCamWnd* cam, brush_t* list) { +static void CamWnd_DrawMoveSelectedBrushSidesForList(const camWndBrushFilterContext_t& filter, brush_t* list) { if (list == NULL || list->next == NULL) { return; } for (brush_t* brush = list->next; brush != list; brush = brush->next) { - if (CamWnd_MenuFilterBrush(cam, brush)) { + if (CamWnd_MenuFilterBrush(filter, brush)) { continue; } if (brush->pPatch || brush->modelHandle > 0 || brush->entityModel) { @@ -3611,7 +3841,7 @@ static void CamWnd_DrawMoveSelectedBrushSidesForList(CCamWnd* cam, brush_t* list } } -static void CamWnd_DrawMoveSelectedBrushSides(CCamWnd* cam) { +static void CamWnd_DrawMoveSelectedBrushSides(const camWndBrushFilterContext_t& filter) { glPushAttrib(GL_CURRENT_BIT); globalImages->BindNull(); glDisable(GL_LIGHTING); @@ -3621,8 +3851,8 @@ static void CamWnd_DrawMoveSelectedBrushSides(CCamWnd* cam) { glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glColor4f(1.0f, 0.0f, 0.0f, 0.25f); - CamWnd_DrawMoveSelectedBrushSidesForList(cam, &active_brushes); - CamWnd_DrawMoveSelectedBrushSidesForList(cam, &selected_brushes); + CamWnd_DrawMoveSelectedBrushSidesForList(filter, &active_brushes); + CamWnd_DrawMoveSelectedBrushSidesForList(filter, &selected_brushes); glPopAttrib(); } @@ -3756,29 +3986,10 @@ void CCamWnd::Cam_Draw() { Cam_BuildMatrix(); - for (brush = active_brushes.next; brush != &active_brushes; brush = brush->next) { + const camWndBrushFilterContext_t brushFilter = CamWnd_MakeBrushFilterContext(this); - if (CullBrush(brush, false)) { - continue; - } - - if (FilterBrush(brush)) { - continue; - } - - if (CamWnd_MenuFilterBrush(this, brush)) { - continue; - } - - if (renderMode) { - if (!(entityMode && brush->owner->eclass->fixedsize)) { - continue; - } - } - - setGLMode(m_Camera.draw_mode); - Brush_Draw(brush); - } + CamWnd_DrawActiveBrushes(this, renderMode, entityMode, brushFilter); + CamWnd_DrawRadiantLightFrustums(this); //glDepthMask ( 1 ); // Ok, write now @@ -3791,7 +4002,7 @@ void CCamWnd::Cam_Draw() { if (!renderMode) { // draw normally for (brush = pList->next; brush != pList; brush = brush->next) { - if (CamWnd_MenuFilterBrush(this, brush)) { + if (CamWnd_MenuFilterBrush(brushFilter, brush)) { continue; } if (brush->pPatch) { @@ -3812,7 +4023,7 @@ void CCamWnd::Cam_Draw() { glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); globalImages->BindNull(); for (brush = pList->next; brush != pList; brush = brush->next) { - if (CamWnd_MenuFilterBrush(this, brush)) { + if (CamWnd_MenuFilterBrush(brushFilter, brush)) { continue; } if (brush->pPatch || brush->modelHandle > 0) { @@ -3860,7 +4071,7 @@ void CCamWnd::Cam_Draw() { glColor4f(1.0f, 0.0f, 0.0f, 0.25f); for (brush = pList->next; brush != pList; brush = brush->next) { - if (CamWnd_MenuFilterBrush(this, brush)) { + if (CamWnd_MenuFilterBrush(brushFilter, brush)) { continue; } if (brush->pPatch || brush->modelHandle > 0) { @@ -3872,7 +4083,7 @@ void CCamWnd::Cam_Draw() { } } - CamWnd_DrawMoveSelectedBrushSides(this); + CamWnd_DrawMoveSelectedBrushSides(brushFilter); CamWnd_FaceExtrudeDrawPreview(this); CamWnd_GizmoDraw(this); @@ -3913,7 +4124,7 @@ void CCamWnd::Cam_Draw() { // draw pointfile glEnable(GL_DEPTH_TEST); - if (CamWnd_TargetLinesVisible(this)) { + if (CamWnd_TargetLinesVisible(brushFilter)) { CamWnd_DrawVisiblePathLines(); } @@ -4196,12 +4407,12 @@ static unsigned int EditorHashBrushGeometry(brush_t* brush, const idVec3& origin return hash; } -static unsigned int EditorHashBModelGeometry(CCamWnd* cam, entity_t* ent) { +static unsigned int EditorHashBModelGeometry(entity_t* ent, const camWndBrushFilterContext_t& filter) { unsigned int hash = EDITOR_RENDER_HASH_INIT; hash = EditorHashString(hash, ValueForKey(ent, "name")); for (brush_t* brush = ent->brushes.onext; brush != &ent->brushes; brush = brush->onext) { - if (FilterBrush(brush) || CamWnd_MenuFilterBrush(cam, brush) || Map_IsBrushFiltered(brush)) { + if (FilterBrush(brush) || CamWnd_MenuFilterBrush(filter, brush) || Map_IsBrushFiltered(brush)) { continue; } const unsigned int brushHash = EditorHashBrushGeometry(brush, ent->origin); @@ -4255,12 +4466,12 @@ static idRenderModel* EditorBuildSingleBrushModel(brush_t* brush, const idVec3& return model; } -static idRenderModel* EditorBuildBModel(CCamWnd* cam, entity_t* ent, const char* name) { +static idRenderModel* EditorBuildBModel(entity_t* ent, const char* name, const camWndBrushFilterContext_t& filter) { idTriList tris(1024); idMatList mats(1024); for (brush_t* brush = ent->brushes.onext; brush != &ent->brushes; brush = brush->onext) { - if (FilterBrush(brush) || CamWnd_MenuFilterBrush(cam, brush) || Map_IsBrushFiltered(brush)) { + if (FilterBrush(brush) || CamWnd_MenuFilterBrush(filter, brush) || Map_IsBrushFiltered(brush)) { continue; } Brush_ToTris(brush, &tris, &mats, false, true); @@ -4600,6 +4811,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) { idDict spawnArgs; const char* name = NULL; editorRenderEntityState_t* entityState = EditorTouchEntityState(ent); + const camWndBrushFilterContext_t brushFilter = CamWnd_MakeBrushFilterContext(this); // The old code used update=false as "tear down and recreate". The // incremental path keeps the handles and uses UpdateEntityDef / @@ -4611,7 +4823,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) { // If the entity is no longer renderable, remove only this entity's defs. if (ent->brushes.onext == &ent->brushes || FilterBrush(ent->brushes.onext) || - CamWnd_MenuFilterBrush(this, ent->brushes.onext) || + CamWnd_MenuFilterBrush(brushFilter, ent->brushes.onext) || CullBrush(ent->brushes.onext, true) || Map_IsBrushFiltered(ent->brushes.onext)) { EditorFreeEntityModelDef(entityState, ent); @@ -4636,7 +4848,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) { // Brush model entity. Rebuild the renderModel only when the // entity-local brush geometry/materials changed. Plain movement // is handled by UpdateEntityDef below. - const unsigned int geometryHash = EditorHashBModelGeometry(this, ent); + const unsigned int geometryHash = EditorHashBModelGeometry(ent, brushFilter); editorRenderBModelState_t* bmodelState = EditorTouchBModelState(ent); idRenderModel* oldModel = NULL; @@ -4646,7 +4858,7 @@ void CCamWnd::BuildEntityRenderState(entity_t* ent, bool update) { idStr::Icmp(bmodelState->modelName.c_str(), name) != 0); if (geometryChanged) { - idRenderModel* newModel = EditorBuildBModel(this, ent, name); + idRenderModel* newModel = EditorBuildBModel(ent, name, brushFilter); if (newModel) { oldModel = bmodelState->model; bmodelState->model = newModel; @@ -5348,6 +5560,7 @@ void CCamWnd::DrawEntityData() { idVec3 color(0, 1, 0); glColor3fv(color.ToFloatPtr()); + const camWndBrushFilterContext_t brushFilter = CamWnd_MakeBrushFilterContext(this); brush_t* brushList = &active_brushes; int pass = 0; while (brushList) { @@ -5361,7 +5574,7 @@ void CCamWnd::DrawEntityData() { continue; } - if (CamWnd_MenuFilterBrush(this, brush)) { + if (CamWnd_MenuFilterBrush(brushFilter, brush)) { continue; } diff --git a/neo/engine/tools/radiant/XYWnd.cpp b/neo/engine/tools/radiant/XYWnd.cpp index 3d6fe511..5bfd957a 100644 --- a/neo/engine/tools/radiant/XYWnd.cpp +++ b/neo/engine/tools/radiant/XYWnd.cpp @@ -5474,97 +5474,140 @@ bool FilterBrush(brush_t *pb) { the lines can be visible when neither end is. Called for both camera view and xy view. ======================================================================================================================= */ -void DrawPathLines(void) { - int i, k; - idVec3 mid, mid1; - entity_t *se, *te; - brush_t *sb, *tb; - const char *psz; - idVec3 dir, s1, s2; - float len, f; - int arrows; - int num_entities; - const char *ent_target[MAX_MAP_ENTITIES]; - entity_t *ent_entity[MAX_MAP_ENTITIES]; +struct pathLineNamedEntity_t { + const char* name; + entity_t* entity; + brush_t* brush; +}; +static bool DrawPathLines_IsTargetKey(const char* key) { + if (key == NULL) { + return false; + } + + if (idStr::Cmp(key, "target") == 0) { + return true; + } + + if (idStr::Cmpn(key, "target", 6) != 0 || key[6] == '\0') { + return false; + } + + for (const char* c = key + 6; *c != '\0'; c++) { + if (*c < '0' || *c > '9') { + return false; + } + } + + return true; +} + +static brush_t* DrawPathLines_FirstBrush(entity_t* ent) { + if (ent == NULL) { + return NULL; + } + + brush_t* brush = ent->brushes.onext; + return (brush != &ent->brushes) ? brush : NULL; +} + +static void DrawPathLines_AppendNamedEntity(idList& namedEntities, idHashIndex& nameHash, entity_t* ent) { + const char* name = ValueForKey(ent, "name"); + if (name == NULL || name[0] == '\0') { + return; + } + + brush_t* brush = DrawPathLines_FirstBrush(ent); + if (brush == NULL) { + return; + } + + pathLineNamedEntity_t named; + named.name = name; + named.entity = ent; + named.brush = brush; + + const int index = namedEntities.Append(named); + nameHash.Add(nameHash.GenerateKey(name, true), index); +} + +static void DrawPathLines_DrawConnection(const pathLineNamedEntity_t& namedTarget, entity_t* source, brush_t* sourceBrush) { + if (namedTarget.entity == NULL || namedTarget.entity->eclass == NULL || source == NULL || sourceBrush == NULL) { + return; + } + + idVec3 mid = namedTarget.brush->owner->origin; + idVec3 mid1 = sourceBrush->owner->origin; + idVec3 dir, s1, s2; + + VectorSubtract(mid1, mid, dir); + const float len = dir.Normalize(); + s1[0] = -dir[1] * 8 + dir[0] * 8; + s2[0] = dir[1] * 8 + dir[0] * 8; + s1[1] = dir[0] * 8 + dir[1] * 8; + s2[1] = -dir[0] * 8 + dir[1] * 8; + + glColor3f(namedTarget.entity->eclass->color[0], namedTarget.entity->eclass->color[1], namedTarget.entity->eclass->color[2]); + + glBegin(GL_LINES); + glVertex3fv(mid.ToFloatPtr()); + glVertex3fv(mid1.ToFloatPtr()); + + const int arrows = (int)(len / 256) + 1; + for (int i = 0; i < arrows; i++) { + const float f = len * (i + 0.5f) / arrows; + + mid1 = mid + (f * dir); + + glVertex3fv(mid1.ToFloatPtr()); + glVertex3f(mid1[0] + s1[0], mid1[1] + s1[1], mid1[2]); + glVertex3fv(mid1.ToFloatPtr()); + glVertex3f(mid1[0] + s2[0], mid1[1] + s2[1], mid1[2]); + } + + glEnd(); +} + +void DrawPathLines(void) { if (g_qeglobals.d_savedinfo.exclude & EXCLUDE_PATHS) { return; } - num_entities = 0; - for (te = entities.next; te != &entities && num_entities != MAX_MAP_ENTITIES; te = te->next) { - for (int i = 0; i < 2048; i++) { - if (i == 0) { - ent_target[num_entities] = ValueForKey(te, "target"); - } else { - ent_target[num_entities] = ValueForKey(te, va("target%i", i)); - } - if (ent_target[num_entities][0]) { - ent_entity[num_entities] = te; - num_entities++; - } else if (i > 16) { - break; - } - } + idList namedEntities; + idHashIndex nameHash(1024, 1024); + namedEntities.SetGranularity(256); + + for (entity_t* ent = entities.next; ent != &entities; ent = ent->next) { + DrawPathLines_AppendNamedEntity(namedEntities, nameHash, ent); } - for (se = entities.next; se != &entities; se = se->next) { - psz = ValueForKey(se, "name"); - - if (psz == NULL || psz[0] == '\0') { + for (entity_t* source = entities.next; source != &entities; source = source->next) { + brush_t* sourceBrush = DrawPathLines_FirstBrush(source); + if (sourceBrush == NULL) { continue; } - sb = se->brushes.onext; - if (sb == &se->brushes) { - continue; - } - - for (k = 0; k < num_entities; k++) { - if (strcmp(ent_target[k], psz)) { + const int numKeys = source->epairs.GetNumKeyVals(); + for (int keyIndex = 0; keyIndex < numKeys; keyIndex++) { + const idKeyValue* kv = source->epairs.GetKeyVal(keyIndex); + if (kv == NULL || !DrawPathLines_IsTargetKey(kv->GetKey().c_str())) { continue; } - te = ent_entity[k]; - tb = te->brushes.onext; - if (tb == &te->brushes) { + const char* targetName = kv->GetValue().c_str(); + if (targetName == NULL || targetName[0] == '\0') { continue; } - mid = sb->owner->origin; - mid1 = tb->owner->origin; - - VectorSubtract(mid1, mid, dir); - len = dir.Normalize(); - s1[0] = -dir[1] * 8 + dir[0] * 8; - s2[0] = dir[1] * 8 + dir[0] * 8; - s1[1] = dir[0] * 8 + dir[1] * 8; - s2[1] = -dir[0] * 8 + dir[1] * 8; - - glColor3f(se->eclass->color[0], se->eclass->color[1], se->eclass->color[2]); - - glBegin(GL_LINES); - glVertex3fv(mid.ToFloatPtr()); - glVertex3fv(mid1.ToFloatPtr()); - - arrows = (int)(len / 256) + 1; - - for (i = 0; i < arrows; i++) { - f = len * (i + 0.5) / arrows; - - mid1 = mid + (f * dir); - - glVertex3fv(mid1.ToFloatPtr()); - glVertex3f(mid1[0] + s1[0], mid1[1] + s1[1], mid1[2]); - glVertex3fv(mid1.ToFloatPtr()); - glVertex3f(mid1[0] + s2[0], mid1[1] + s2[1], mid1[2]); + const int hashKey = nameHash.GenerateKey(targetName, true); + for (int index = nameHash.First(hashKey); index != -1; index = nameHash.Next(index)) { + const pathLineNamedEntity_t& namedTarget = namedEntities[index]; + if (strcmp(namedTarget.name, targetName) == 0) { + DrawPathLines_DrawConnection(namedTarget, source, sourceBrush); + } } - - glEnd(); } } - - return; } //