diff --git a/Editor/Editor.cpp b/Editor/Editor.cpp index d7facfba5..26309d39d 100644 --- a/Editor/Editor.cpp +++ b/Editor/Editor.cpp @@ -1309,6 +1309,8 @@ void EditorComponent::Update(float dt) SAFE_DELETE(history); } + emitterWnd->UpdateData(); + __super::Update(dt); renderPath->Update(dt); diff --git a/Editor/EmitterWindow.cpp b/Editor/EmitterWindow.cpp index 0851caa49..cdc2cd1eb 100644 --- a/Editor/EmitterWindow.cpp +++ b/Editor/EmitterWindow.cpp @@ -54,11 +54,6 @@ EmitterWindow::EmitterWindow(wiGUI* gui) : GUI(gui) if (emitter != nullptr) { emitter->SetMaxParticleCount((uint32_t)args.iValue); - - stringstream ss(""); - ss.precision(2); - ss << "Memort Budget: " << emitter->GetMemorySizeInBytes() / 1024.0f / 1024.0f << " MB"; - memoryBudgetLabel->SetText(ss.str()); } }); maxParticlesSlider->SetEnabled(false); @@ -195,6 +190,29 @@ EmitterWindow::EmitterWindow(wiGUI* gui) : GUI(gui) emitMotionBlurSlider->SetTooltip("Set the motion blur amount for the particle system."); emitterWindow->AddWidget(emitMotionBlurSlider); + y += 30; + + ////////////////////////////////////////////////////////////////////////////////////////////////// + + debugCheckBox = new wiCheckBox("DEBUG: "); + debugCheckBox->SetPos(XMFLOAT2(x, y += step)); + debugCheckBox->OnClick([&](wiEventArgs args) { + auto emitter = GetEmitter(); + if (emitter != nullptr) + { + emitter->DEBUG = args.bValue; + } + }); + debugCheckBox->SetCheck(false); + debugCheckBox->SetTooltip("Enable debug info for the emitter. This involves reading back GPU data, so rendering can slow down."); + emitterWindow->AddWidget(debugCheckBox); + + debugDataLabel = new wiLabel("EmitterDebugDataLabel"); + debugDataLabel->SetText("Enable DEBUG to view debugging data"); + debugDataLabel->SetSize(XMFLOAT2(300, 100)); + debugDataLabel->SetPos(XMFLOAT2(x + step, y)); + emitterWindow->AddWidget(debugDataLabel); + emitterWindow->Translate(XMFLOAT3(200, 50, 0)); emitterWindow->SetVisible(false); @@ -212,6 +230,12 @@ EmitterWindow::~EmitterWindow() void EmitterWindow::SetObject(Object* obj) { + // first try to turn off any debug readbacks for emitters: + if (GetEmitter() != nullptr) + { + GetEmitter()->DEBUG = false; + } + object = obj; emitterComboBox->ClearItems(); @@ -229,11 +253,6 @@ void EmitterWindow::SetObject(Object* obj) { maxParticlesSlider->SetValue((float)emitter->GetMaxParticleCount()); - stringstream ss(""); - ss.precision(2); - ss << "Memort Budget: " << emitter->GetMemorySizeInBytes() / 1024.0f / 1024.0f << " MB"; - memoryBudgetLabel->SetText(ss.str()); - emitCountSlider->SetValue(emitter->count); emitSizeSlider->SetValue(emitter->size); emitRotationSlider->SetValue(emitter->rotation); @@ -277,3 +296,33 @@ wiEmittedParticle* EmitterWindow::GetEmitter() return nullptr; } + +void EmitterWindow::UpdateData() +{ + auto emitter = GetEmitter(); + if (emitter == nullptr) + { + return; + } + + if (emitter->DEBUG) + { + auto data = emitter->GetDebugData(); + + stringstream ss(""); + ss << "Alive Particle Count [CURRENT] = " << data.aliveCount_CURRENT << endl; + ss << "Alive Particle Count [NEW] = " << data.aliveCount_NEW << endl; + ss << "Dead Particle Count = " << data.deadCount << endl; + ss << "GPU Emit count = " << data.realEmitCount << endl; + debugDataLabel->SetText(ss.str()); + } + else + { + debugDataLabel->SetText("Enable DEBUG to view debugging data"); + } + + stringstream ss(""); + ss.precision(2); + ss << "Memort Budget: " << emitter->GetMemorySizeInBytes() / 1024.0f / 1024.0f << " MB"; + memoryBudgetLabel->SetText(ss.str()); +} diff --git a/Editor/EmitterWindow.h b/Editor/EmitterWindow.h index ebb5a8258..e131b8528 100644 --- a/Editor/EmitterWindow.h +++ b/Editor/EmitterWindow.h @@ -18,6 +18,7 @@ public: ~EmitterWindow(); void SetObject(Object* obj); + void UpdateData(); Object* object; wiEmittedParticle* GetEmitter(); @@ -38,5 +39,8 @@ public: wiSlider* emitRandomnessSlider; wiSlider* emitLifeRandomnessSlider; wiSlider* emitMotionBlurSlider; + + wiCheckBox* debugCheckBox; + wiLabel* debugDataLabel; }; diff --git a/WickedEngine/ArchiveVersionHistory.txt b/WickedEngine/ArchiveVersionHistory.txt index 809bf5f59..2718ce02e 100644 --- a/WickedEngine/ArchiveVersionHistory.txt +++ b/WickedEngine/ArchiveVersionHistory.txt @@ -1,5 +1,6 @@ This file contains changelog of wiArchive versions +11: serialize additional emitter properties 10: serialize force fields 9: refactored emitter particle system 8: refactored vertex wind weight diff --git a/WickedEngine/ShaderInterop_EmittedParticle.h b/WickedEngine/ShaderInterop_EmittedParticle.h index 1229ae356..bacb69ab9 100644 --- a/WickedEngine/ShaderInterop_EmittedParticle.h +++ b/WickedEngine/ShaderInterop_EmittedParticle.h @@ -17,6 +17,13 @@ struct Particle float2 mirror; }; +struct ParticleCounters +{ + uint aliveCount_CURRENT; + uint deadCount; + uint aliveCount_NEW; + uint realEmitCount; +}; CBUFFER(EmittedParticleCB, CBSLOT_OTHER_EMITTEDPARTICLE) { diff --git a/WickedEngine/emittedparticle_drawargsCS.hlsl b/WickedEngine/emittedparticle_drawargsCS.hlsl index 46cdc6d24..868e5a6b0 100644 --- a/WickedEngine/emittedparticle_drawargsCS.hlsl +++ b/WickedEngine/emittedparticle_drawargsCS.hlsl @@ -1,18 +1,16 @@ #include "globals.hlsli" #include "ShaderInterop_EmittedParticle.h" -RWSTRUCTUREDBUFFER(counterBuffer, uint4, 4); // alive count, dead count, new alive count -RWRAWBUFFER(indirectDrawBuffer, 6); // indirectdrawinstanced args +RWSTRUCTUREDBUFFER(counterBuffer, ParticleCounters, 4); +RWRAWBUFFER(indirectDrawBuffer, 5); // indirectdrawinstanced args [numthreads(1, 1, 1)] void main(uint3 DTid : SV_DispatchThreadID) { - uint aliveCount_NEW = counterBuffer[0][2]; - // Fill Draw arguments for drawing: // VertexCountPerInstance; // InstanceCount; // StartVertexLocation; // StartInstanceLocation; - indirectDrawBuffer.Store4(0, uint4(aliveCount_NEW * 6, 1, 0, 0)); + indirectDrawBuffer.Store4(24, uint4(counterBuffer[0].aliveCount_NEW * 6, 1, 0, 0)); } \ No newline at end of file diff --git a/WickedEngine/emittedparticle_emitCS.hlsl b/WickedEngine/emittedparticle_emitCS.hlsl index a22b52c52..002509ee6 100644 --- a/WickedEngine/emittedparticle_emitCS.hlsl +++ b/WickedEngine/emittedparticle_emitCS.hlsl @@ -5,7 +5,7 @@ RWSTRUCTUREDBUFFER(particleBuffer, Particle, 0); RWSTRUCTUREDBUFFER(aliveBuffer_OLD, uint, 1); RWSTRUCTUREDBUFFER(aliveBuffer_NEW, uint, 2); RWSTRUCTUREDBUFFER(deadBuffer, uint, 3); -RWSTRUCTUREDBUFFER(counterBuffer, uint4, 4); +RWSTRUCTUREDBUFFER(counterBuffer, ParticleCounters, 4); TEXTURE2D(randomTex, float4, TEXSLOT_ONDEMAND0); TYPEDBUFFER(meshIndexBuffer, uint, TEXSLOT_ONDEMAND1); @@ -16,7 +16,7 @@ RAWBUFFER(meshVertexBuffer_NOR, TEXSLOT_ONDEMAND3); [numthreads(THREADCOUNT_EMIT, 1, 1)] void main(uint3 DTid : SV_DispatchThreadID) { - uint emitCount = counterBuffer[0][3]; + uint emitCount = counterBuffer[0].realEmitCount; if(DTid.x < emitCount) { @@ -92,7 +92,7 @@ void main(uint3 DTid : SV_DispatchThreadID) // new particle index retrieved from dead list (pop): uint deadCount; - InterlockedAdd(counterBuffer[0][1], -1, deadCount); + InterlockedAdd(counterBuffer[0].deadCount, -1, deadCount); uint newParticleIndex = deadBuffer[deadCount - 1]; // write out the new particle: @@ -100,7 +100,7 @@ void main(uint3 DTid : SV_DispatchThreadID) // and add index to the alive list (push): uint aliveCount_OLD; - InterlockedAdd(counterBuffer[0][0], 1, aliveCount_OLD); + InterlockedAdd(counterBuffer[0].aliveCount_CURRENT, 1, aliveCount_OLD); aliveBuffer_OLD[aliveCount_OLD] = newParticleIndex; } } diff --git a/WickedEngine/emittedparticle_kickoffUpdateCS.hlsl b/WickedEngine/emittedparticle_kickoffUpdateCS.hlsl index c03d24876..2e92229ea 100644 --- a/WickedEngine/emittedparticle_kickoffUpdateCS.hlsl +++ b/WickedEngine/emittedparticle_kickoffUpdateCS.hlsl @@ -1,13 +1,13 @@ #include "globals.hlsli" #include "ShaderInterop_EmittedParticle.h" -RWSTRUCTUREDBUFFER(counterBuffer, uint4, 4); // alive count, dead count, new alive count, realEmitCount +RWSTRUCTUREDBUFFER(counterBuffer, ParticleCounters, 4); RWRAWBUFFER(indirectDispatchBuffer, 5); // indirect kickoff args [numthreads(1, 1, 1)] void main(uint3 DTid : SV_DispatchThreadID) { - uint deadCount = counterBuffer[0][1]; + uint deadCount = counterBuffer[0].deadCount; // we can not emit more than there are free slots in the dead list: uint realEmitCount = min(deadCount, xEmitCount); @@ -15,12 +15,12 @@ void main(uint3 DTid : SV_DispatchThreadID) // Fill dispatch argument buffer for simulation: indirectDispatchBuffer.Store3(0, uint3(ceil((float)realEmitCount / (float)THREADCOUNT_EMIT), 1, 1)); - // copy new alivelistcount to oldalivelistcount: - counterBuffer[0][0] = counterBuffer[0][2]; + // copy new alivelistcount to current alivelistcount: + counterBuffer[0].aliveCount_CURRENT = counterBuffer[0].aliveCount_NEW; // also reset the new alive list count: - counterBuffer[0][2] = 0; + counterBuffer[0].aliveCount_NEW = 0; // and write real emit count: - counterBuffer[0][3] = realEmitCount; + counterBuffer[0].realEmitCount = realEmitCount; } \ No newline at end of file diff --git a/WickedEngine/emittedparticle_simulateCS.hlsl b/WickedEngine/emittedparticle_simulateCS.hlsl index 988ac924c..4f82c0cde 100644 --- a/WickedEngine/emittedparticle_simulateCS.hlsl +++ b/WickedEngine/emittedparticle_simulateCS.hlsl @@ -5,7 +5,7 @@ RWSTRUCTUREDBUFFER(particleBuffer, Particle, 0); RWSTRUCTUREDBUFFER(aliveBuffer_OLD, uint, 1); RWSTRUCTUREDBUFFER(aliveBuffer_NEW, uint, 2); RWSTRUCTUREDBUFFER(deadBuffer, uint, 3); -RWSTRUCTUREDBUFFER(counterBuffer, uint4, 4); +RWSTRUCTUREDBUFFER(counterBuffer, ParticleCounters, 4); #define NUM_LDS_FORCEFIELDS 32 struct LDS_ForceField @@ -26,7 +26,7 @@ void main(uint3 DTid : SV_DispatchThreadID, uint Gid : SV_GroupIndex) // Read alive particle count and store in LDS: if (Gid == 0) { - aliveParticleCount = counterBuffer[0][0]; + aliveParticleCount = counterBuffer[0].aliveCount_CURRENT; } // Load the forcefields into LDS: @@ -91,14 +91,14 @@ void main(uint3 DTid : SV_DispatchThreadID, uint Gid : SV_GroupIndex) // add to new alive list: uint newAliveIndex; - InterlockedAdd(counterBuffer[0][2], 1, newAliveIndex); + InterlockedAdd(counterBuffer[0].aliveCount_NEW, 1, newAliveIndex); aliveBuffer_NEW[newAliveIndex] = particleIndex; } else { // kill: uint deadIndex; - InterlockedAdd(counterBuffer[0][1], 1, deadIndex); + InterlockedAdd(counterBuffer[0].deadCount, 1, deadIndex); deadBuffer[deadIndex] = particleIndex; } } diff --git a/WickedEngine/emittedparticle_simulateargsCS.hlsl b/WickedEngine/emittedparticle_simulateargsCS.hlsl index 6bf984a1f..d2e2754d3 100644 --- a/WickedEngine/emittedparticle_simulateargsCS.hlsl +++ b/WickedEngine/emittedparticle_simulateargsCS.hlsl @@ -1,14 +1,12 @@ #include "globals.hlsli" #include "ShaderInterop_EmittedParticle.h" -RWSTRUCTUREDBUFFER(counterBuffer, uint4, 4); // alive count, dead count, new alive count, realEmitCount -RWRAWBUFFER(indirectDispatchBuffer, 5); // indirectdispatch args +RWSTRUCTUREDBUFFER(counterBuffer, ParticleCounters, 4); +RWRAWBUFFER(indirectDispatchBuffer, 5); // indirect simulation args [numthreads(1, 1, 1)] void main( uint3 DTid : SV_DispatchThreadID ) { - uint aliveCount_OLD = counterBuffer[0][0]; - // Fill dispatch argument buffer for simulation: - indirectDispatchBuffer.Store3(0, uint3(ceil((float)aliveCount_OLD / (float)THREADCOUNT_SIMULATION), 1, 1)); + indirectDispatchBuffer.Store3(12, uint3(ceil((float)counterBuffer[0].aliveCount_CURRENT / (float)THREADCOUNT_SIMULATION), 1, 1)); } \ No newline at end of file diff --git a/WickedEngine/wiArchive.cpp b/WickedEngine/wiArchive.cpp index faa86f82c..9c04f9701 100644 --- a/WickedEngine/wiArchive.cpp +++ b/WickedEngine/wiArchive.cpp @@ -7,7 +7,7 @@ using namespace std; // this should always be only INCREMENTED and only if a new serialization is implemeted somewhere! -uint64_t __archiveVersion = 10; +uint64_t __archiveVersion = 11; // this is the version number of which below the archive is not compatible with the current version uint64_t __archiveVersionBarrier = 1; diff --git a/WickedEngine/wiEmittedParticle.cpp b/WickedEngine/wiEmittedParticle.cpp index 2749a0136..c34752c81 100644 --- a/WickedEngine/wiEmittedParticle.cpp +++ b/WickedEngine/wiEmittedParticle.cpp @@ -50,9 +50,9 @@ wiEmittedParticle::wiEmittedParticle() SAFE_INIT(aliveList[1]); SAFE_INIT(deadList); SAFE_INIT(counterBuffer); - SAFE_INIT(indirectDispatchBuffer); - SAFE_INIT(indirectDrawBuffer); + SAFE_INIT(indirectBuffers); SAFE_INIT(constantBuffer); + SAFE_INIT(debugDataReadbackBuffer); SetMaxParticleCount(10000); } @@ -91,9 +91,9 @@ wiEmittedParticle::wiEmittedParticle(const std::string& newName, const std::stri SAFE_INIT(aliveList[1]); SAFE_INIT(deadList); SAFE_INIT(counterBuffer); - SAFE_INIT(indirectDispatchBuffer); - SAFE_INIT(indirectDrawBuffer); + SAFE_INIT(indirectBuffers); SAFE_INIT(constantBuffer); + SAFE_INIT(debugDataReadbackBuffer); SetMaxParticleCount(10000); } @@ -121,38 +121,44 @@ wiEmittedParticle::wiEmittedParticle(const wiEmittedParticle& other) SAFE_INIT(aliveList[1]); SAFE_INIT(deadList); SAFE_INIT(counterBuffer); - SAFE_INIT(indirectDispatchBuffer); - SAFE_INIT(indirectDrawBuffer); + SAFE_INIT(indirectBuffers); SAFE_INIT(constantBuffer); + SAFE_INIT(debugDataReadbackBuffer); SetMaxParticleCount(other.GetMaxParticleCount()); } void wiEmittedParticle::SetMaxParticleCount(uint32_t value) { + buffersUpToDate = false; MAX_PARTICLES = value; - CreateSelfBuffers(); } void wiEmittedParticle::CreateSelfBuffers() { + if (buffersUpToDate) + { + return; + } + buffersUpToDate = true; + SAFE_DELETE(particleBuffer); SAFE_DELETE(aliveList[0]); SAFE_DELETE(aliveList[1]); SAFE_DELETE(deadList); SAFE_DELETE(counterBuffer); - SAFE_DELETE(indirectDispatchBuffer); - SAFE_DELETE(indirectDrawBuffer); + SAFE_DELETE(indirectBuffers); SAFE_DELETE(constantBuffer); + SAFE_DELETE(debugDataReadbackBuffer); particleBuffer = new GPUBuffer; aliveList[0] = new GPUBuffer; aliveList[1] = new GPUBuffer; deadList = new GPUBuffer; counterBuffer = new GPUBuffer; - indirectDispatchBuffer = new GPUBuffer; - indirectDrawBuffer = new GPUBuffer; + indirectBuffers = new GPUBuffer; constantBuffer = new GPUBuffer; + debugDataReadbackBuffer = new GPUBuffer; GPUBufferDesc bd; bd.Usage = USAGE_DEFAULT; @@ -182,8 +188,13 @@ void wiEmittedParticle::CreateSelfBuffers() data.pSysMem = nullptr; - uint32_t counters[] = { 0, MAX_PARTICLES, 0, 0 }; // aliveCount, deadCount, newAliveCount, realEmitCount - data.pSysMem = counters; + ParticleCounters counters; + counters.aliveCount_CURRENT = 0; + counters.deadCount = MAX_PARTICLES; + counters.aliveCount_NEW = 0; + counters.realEmitCount = 0; + + data.pSysMem = &counters; bd.ByteWidth = sizeof(counters); bd.StructureByteStride = sizeof(counters); wiRenderer::GetDevice()->CreateBuffer(&bd, &data, counterBuffer); @@ -192,10 +203,8 @@ void wiEmittedParticle::CreateSelfBuffers() bd.BindFlags = BIND_UNORDERED_ACCESS; bd.MiscFlags = RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS | RESOURCE_MISC_DRAWINDIRECT_ARGS; - bd.ByteWidth = sizeof(wiGraphicsTypes::IndirectDrawArgsInstanced); - wiRenderer::GetDevice()->CreateBuffer(&bd, nullptr, indirectDrawBuffer); - bd.ByteWidth = sizeof(wiGraphicsTypes::IndirectDispatchArgs); - wiRenderer::GetDevice()->CreateBuffer(&bd, nullptr, indirectDispatchBuffer); + bd.ByteWidth = sizeof(wiGraphicsTypes::IndirectDispatchArgs) + sizeof(wiGraphicsTypes::IndirectDispatchArgs) + sizeof(wiGraphicsTypes::IndirectDrawArgsInstanced); + wiRenderer::GetDevice()->CreateBuffer(&bd, nullptr, indirectBuffers); bd.Usage = USAGE_DYNAMIC; @@ -204,6 +213,14 @@ void wiEmittedParticle::CreateSelfBuffers() bd.CPUAccessFlags = CPU_ACCESS_WRITE; bd.MiscFlags = 0; wiRenderer::GetDevice()->CreateBuffer(&bd, nullptr, constantBuffer); + + { + GPUBufferDesc debugBufDesc = counterBuffer->GetDesc(); + debugBufDesc.Usage = USAGE_STAGING; + debugBufDesc.CPUAccessFlags = CPU_ACCESS_READ; + debugBufDesc.BindFlags = 0; + wiRenderer::GetDevice()->CreateBuffer(&debugBufDesc, nullptr, debugDataReadbackBuffer); + } } uint32_t wiEmittedParticle::GetMemorySizeInBytes() const @@ -215,8 +232,7 @@ uint32_t wiEmittedParticle::GetMemorySizeInBytes() const retVal += aliveList[1]->GetDesc().ByteWidth; retVal += deadList->GetDesc().ByteWidth; retVal += counterBuffer->GetDesc().ByteWidth; - retVal += indirectDispatchBuffer->GetDesc().ByteWidth; - retVal += indirectDrawBuffer->GetDesc().ByteWidth; + retVal += indirectBuffers->GetDesc().ByteWidth; retVal += constantBuffer->GetDesc().ByteWidth; return retVal; @@ -240,6 +256,8 @@ void wiEmittedParticle::Burst(float num) void wiEmittedParticle::UpdateRenderData(GRAPHICSTHREAD threadID) { + CreateSelfBuffers(); + GraphicsDevice* device = wiRenderer::GetDevice(); device->EventBegin("UpdateEmittedParticles", threadID); @@ -265,12 +283,11 @@ void wiEmittedParticle::UpdateRenderData(GRAPHICSTHREAD threadID) const GPUUnorderedResource* uavs[] = { static_cast(particleBuffer), - static_cast(aliveList[0]), // last alivelist - static_cast(aliveList[1]), // new alivelist + static_cast(aliveList[0]), // CURRENT alivelist + static_cast(aliveList[1]), // NEW alivelist static_cast(deadList), static_cast(counterBuffer), - static_cast(indirectDispatchBuffer), - static_cast(indirectDrawBuffer), + static_cast(indirectBuffers), }; device->BindUnorderedAccessResourcesCS(uavs, 0, ARRAYSIZE(uavs), threadID); @@ -289,15 +306,15 @@ void wiEmittedParticle::UpdateRenderData(GRAPHICSTHREAD threadID) // emit the required amount if there are free slots in dead list device->BindCS(emitCS, threadID); - device->DispatchIndirect(indirectDispatchBuffer, 0, threadID); + device->DispatchIndirect(indirectBuffers, 0, threadID); - // kick off simulation based on OLD alivelist count + // kick off simulation based on CURRENT alivelist count device->BindCS(simulateargsCS, threadID); device->Dispatch(1, 1, 1, threadID); - // update OLD alive list, write NEW alive list + // update CURRENT alive list, write NEW alive list device->BindCS(simulateCS, threadID); - device->DispatchIndirect(indirectDispatchBuffer, 0, threadID); + device->DispatchIndirect(indirectBuffers, sizeof(wiGraphicsTypes::IndirectDispatchArgs), threadID); // update the draw arguments device->BindCS(drawargsCS, threadID); @@ -310,9 +327,14 @@ void wiEmittedParticle::UpdateRenderData(GRAPHICSTHREAD threadID) device->EventEnd(threadID); - // Swap OLD alivelist with NEW alivelist + // Swap CURRENT alivelist with NEW alivelist SwapPtr(aliveList[0], aliveList[1]); emit -= (UINT)emit; + + if (DEBUG) + { + device->DownloadBuffer(counterBuffer, debugDataReadbackBuffer, &debugData, threadID, false); + } } void wiEmittedParticle::Draw(GRAPHICSTHREAD threadID) @@ -342,8 +364,7 @@ void wiEmittedParticle::Draw(GRAPHICSTHREAD threadID) device->BindResourcePS(material->texture, TEXSLOT_ONDEMAND0, threadID); } - //device->Draw((int)points.size() * 6, threadID); - device->DrawInstancedIndirect(indirectDrawBuffer, 0, threadID); + device->DrawInstancedIndirect(indirectBuffers, sizeof(wiGraphicsTypes::IndirectDispatchArgs) * 2, threadID); device->EventEnd(threadID); } @@ -356,9 +377,9 @@ void wiEmittedParticle::CleanUp() SAFE_DELETE(aliveList[1]); SAFE_DELETE(deadList); SAFE_DELETE(counterBuffer); - SAFE_DELETE(indirectDispatchBuffer); - SAFE_DELETE(indirectDrawBuffer); + SAFE_DELETE(indirectBuffers); SAFE_DELETE(constantBuffer); + SAFE_DELETE(debugDataReadbackBuffer); } @@ -527,6 +548,11 @@ void wiEmittedParticle::Serialize(wiArchive& archive) string lightName; archive >> lightName; } + if (archive.GetVersion() >= 11) + { + archive >> MAX_PARTICLES; + archive >> SORTING; + } } else @@ -544,5 +570,10 @@ void wiEmittedParticle::Serialize(wiArchive& archive) archive << scaleY; archive << rotation; archive << motionBlurAmount; + if (archive.GetVersion() >= 11) + { + archive << MAX_PARTICLES; + archive << SORTING; + } } } diff --git a/WickedEngine/wiEmittedParticle.h b/WickedEngine/wiEmittedParticle.h index da12fe03f..558712d9b 100644 --- a/WickedEngine/wiEmittedParticle.h +++ b/WickedEngine/wiEmittedParticle.h @@ -17,13 +17,14 @@ class wiArchive; class wiEmittedParticle { private: + ParticleCounters debugData = {}; + wiGraphicsTypes::GPUBuffer* debugDataReadbackBuffer; wiGraphicsTypes::GPUBuffer* particleBuffer; wiGraphicsTypes::GPUBuffer* aliveList[2]; wiGraphicsTypes::GPUBuffer* deadList; wiGraphicsTypes::GPUBuffer* counterBuffer; - wiGraphicsTypes::GPUBuffer* indirectDispatchBuffer; - wiGraphicsTypes::GPUBuffer* indirectDrawBuffer; + wiGraphicsTypes::GPUBuffer* indirectBuffers; // kickoff, simulation, draw wiGraphicsTypes::GPUBuffer* constantBuffer; void CreateSelfBuffers(); @@ -42,6 +43,7 @@ private: float emit; + bool buffersUpToDate = false; uint32_t MAX_PARTICLES; public: @@ -60,6 +62,11 @@ public: void Draw(GRAPHICSTHREAD threadID); void CleanUp(); + bool DEBUG = false; + ParticleCounters GetDebugData() { return debugData; } + + bool SORTING = false; + std::string name; Object* object; std::string materialName; diff --git a/WickedEngine/wiRenderer.cpp b/WickedEngine/wiRenderer.cpp index 38a7114ad..f9b6dfed6 100644 --- a/WickedEngine/wiRenderer.cpp +++ b/WickedEngine/wiRenderer.cpp @@ -1394,10 +1394,10 @@ void wiRenderer::SetUpStates() bd.AlphaToCoverageEnable = false; GetDevice()->CreateBlendState(&bd, blendStates[BSTYPE_ENVIRONMENTALLIGHT]); - bd.RenderTarget[0].SrcBlend = BLEND_INV_DEST_COLOR; - bd.RenderTarget[0].DestBlend = BLEND_ZERO; + bd.RenderTarget[0].SrcBlend = BLEND_INV_SRC_COLOR; + bd.RenderTarget[0].DestBlend = BLEND_INV_DEST_COLOR; bd.RenderTarget[0].BlendOp = BLEND_OP_ADD; - bd.RenderTarget[0].SrcBlendAlpha = BLEND_ZERO; + bd.RenderTarget[0].SrcBlendAlpha = BLEND_ONE; bd.RenderTarget[0].DestBlendAlpha = BLEND_ONE; bd.RenderTarget[0].BlendOpAlpha = BLEND_OP_ADD; bd.RenderTarget[0].BlendEnable = true; @@ -1856,119 +1856,10 @@ void wiRenderer::UpdateRenderData(GRAPHICSTHREAD threadID) UpdateFrameCB(threadID); BindPersistentState(threadID); - - wiProfiler::GetInstance().BeginRange("Skinning", wiProfiler::DOMAIN_GPU, threadID); - GetDevice()->EventBegin("Skinning", threadID); - { - bool streamOutSetUp = false; - - for (Model* model : GetScene().models) - { - // Update material constant buffers: - MaterialCB materialGPUData; - for (auto& it : model->materials) - { - Material* material = it.second; - materialGPUData.Create(*material); - // These will probably not change every time so only issue a GPU memory update if it is necessary: - if (memcmp(&material->gpuData, &materialGPUData, sizeof(MaterialCB)) != 0) - { - material->gpuData = materialGPUData; - GetDevice()->UpdateBuffer(&material->constantBuffer, &materialGPUData, threadID); - } - } - - // Skinning: - for (MeshCollection::iterator iter = model->meshes.begin(); iter != model->meshes.end(); ++iter) - { - Mesh* mesh = iter->second; - - if (mesh->hasArmature() && !mesh->hasDynamicVB() && mesh->renderable && !mesh->vertices_POS.empty() - && mesh->streamoutBuffer_POS.IsValid() && mesh->vertexBuffer_POS.IsValid()) - { - Armature* armature = mesh->armature; - - if (!streamOutSetUp) - { - // Set up skinning shader - streamOutSetUp = true; - GetDevice()->BindVertexLayout(nullptr, threadID); - GPUBuffer* vbs[] = { - nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr - }; - const UINT strides[] = { - 0,0,0,0,0,0,0,0 - }; - GetDevice()->BindVertexBuffers(vbs, 0, ARRAYSIZE(vbs), strides, nullptr, threadID); - GetDevice()->BindCS(computeShaders[CSTYPE_SKINNING], threadID); - } - - // Upload bones for skinning to shader - for (unsigned int k = 0; k < armature->boneCollection.size(); k++) - { - armature->boneData[k].Create(armature->boneCollection[k]->boneRelativity); - } - GetDevice()->UpdateBuffer(&armature->boneBuffer, armature->boneData.data(), threadID, (int)(sizeof(Armature::ShaderBoneType) * armature->boneCollection.size())); - GetDevice()->BindResourceCS(&armature->boneBuffer, SKINNINGSLOT_IN_BONEBUFFER, threadID); - - // Do the skinning - const GPUResource* vbs[] = { - static_cast(&mesh->vertexBuffer_POS), - static_cast(&mesh->vertexBuffer_NOR), - static_cast(&mesh->vertexBuffer_BON), - }; - const GPUUnorderedResource* sos[] = { - static_cast(&mesh->streamoutBuffer_POS), - static_cast(&mesh->streamoutBuffer_NOR), - static_cast(&mesh->streamoutBuffer_PRE), - }; - - GetDevice()->BindResourcesCS(vbs, SKINNINGSLOT_IN_VERTEX_POS, ARRAYSIZE(vbs), threadID); - GetDevice()->BindUnorderedAccessResourcesCS(sos, 0, ARRAYSIZE(sos), threadID); - - GetDevice()->Dispatch((UINT)ceilf((float)mesh->vertices_POS.size() / SKINNING_COMPUTE_THREADCOUNT), 1, 1, threadID); - - } - else if (mesh->hasDynamicVB()) - { - // Upload CPU skinned vertex buffer (Soft body VB) - mesh->bufferOffset_POS = GetDevice()->AppendRingBuffer(dynamicVertexBufferPool, mesh->vertices_Transformed_POS.data(), sizeof(Mesh::Vertex_POS)*mesh->vertices_Transformed_POS.size(), threadID); - mesh->bufferOffset_NOR = GetDevice()->AppendRingBuffer(dynamicVertexBufferPool, mesh->vertices_Transformed_NOR.data(), sizeof(Mesh::Vertex_NOR)*mesh->vertices_Transformed_NOR.size(), threadID); - mesh->bufferOffset_PRE = GetDevice()->AppendRingBuffer(dynamicVertexBufferPool, mesh->vertices_Transformed_PRE.data(), sizeof(Mesh::Vertex_POS)*mesh->vertices_Transformed_PRE.size(), threadID); - } - } - } - - if (streamOutSetUp) - { - // Unload skinning shader - GetDevice()->BindCS(nullptr, threadID); - GetDevice()->UnBindUnorderedAccessResources(0, 3, threadID); - GetDevice()->UnBindResources(SKINNINGSLOT_IN_VERTEX_POS, 4, threadID); - } - - } - GetDevice()->EventEnd(threadID); - wiProfiler::GetInstance().EndRange(threadID); // skinning - - - RefreshEnvProbes(threadID); - - // Bind environment probes: - { - const GPUResource* envMaps[] = { - globalEnvProbes[0] == nullptr ? enviroMap : globalEnvProbes[0]->cubeMap.GetTexture(), - globalEnvProbes[1] == nullptr ? enviroMap : globalEnvProbes[1]->cubeMap.GetTexture(), - }; - GetDevice()->BindResourcesPS(envMaps, TEXSLOT_ENV0, ARRAYSIZE(envMaps), threadID); - GetDevice()->BindResourcesCS(envMaps, TEXSLOT_ENV0, ARRAYSIZE(envMaps), threadID); - } - + ManageDecalAtlas(threadID); const FrameCulling& mainCameraCulling = frameCullings[getCamera()]; - ManageDecalAtlas(threadID); - // Fill Light Array with lights + decals in the frustum: { const CulledList& culledLights = mainCameraCulling.culledLights; @@ -2125,7 +2016,7 @@ void wiRenderer::UpdateRenderData(GRAPHICSTHREAD threadID) GetDevice()->BindResourcesCS(resources, SBSLOT_ENTITYARRAY, ARRAYSIZE(resources), threadID); } - // Update visible particlesystem vertices: + // Particle system simulation/sorting/culling: for (auto& x : emitterSystems) { x->UpdateRenderData(threadID); @@ -2134,6 +2025,114 @@ void wiRenderer::UpdateRenderData(GRAPHICSTHREAD threadID) { hair->ComputeCulling(getCamera(), threadID); } + + + wiProfiler::GetInstance().BeginRange("Skinning", wiProfiler::DOMAIN_GPU, threadID); + GetDevice()->EventBegin("Skinning", threadID); + { + bool streamOutSetUp = false; + + for (Model* model : GetScene().models) + { + // Update material constant buffers: + MaterialCB materialGPUData; + for (auto& it : model->materials) + { + Material* material = it.second; + materialGPUData.Create(*material); + // These will probably not change every time so only issue a GPU memory update if it is necessary: + if (memcmp(&material->gpuData, &materialGPUData, sizeof(MaterialCB)) != 0) + { + material->gpuData = materialGPUData; + GetDevice()->UpdateBuffer(&material->constantBuffer, &materialGPUData, threadID); + } + } + + // Skinning: + for (MeshCollection::iterator iter = model->meshes.begin(); iter != model->meshes.end(); ++iter) + { + Mesh* mesh = iter->second; + + if (mesh->hasArmature() && !mesh->hasDynamicVB() && mesh->renderable && !mesh->vertices_POS.empty() + && mesh->streamoutBuffer_POS.IsValid() && mesh->vertexBuffer_POS.IsValid()) + { + Armature* armature = mesh->armature; + + if (!streamOutSetUp) + { + // Set up skinning shader + streamOutSetUp = true; + GetDevice()->BindVertexLayout(nullptr, threadID); + GPUBuffer* vbs[] = { + nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr,nullptr + }; + const UINT strides[] = { + 0,0,0,0,0,0,0,0 + }; + GetDevice()->BindVertexBuffers(vbs, 0, ARRAYSIZE(vbs), strides, nullptr, threadID); + GetDevice()->BindCS(computeShaders[CSTYPE_SKINNING], threadID); + } + + // Upload bones for skinning to shader + for (unsigned int k = 0; k < armature->boneCollection.size(); k++) + { + armature->boneData[k].Create(armature->boneCollection[k]->boneRelativity); + } + GetDevice()->UpdateBuffer(&armature->boneBuffer, armature->boneData.data(), threadID, (int)(sizeof(Armature::ShaderBoneType) * armature->boneCollection.size())); + GetDevice()->BindResourceCS(&armature->boneBuffer, SKINNINGSLOT_IN_BONEBUFFER, threadID); + + // Do the skinning + const GPUResource* vbs[] = { + static_cast(&mesh->vertexBuffer_POS), + static_cast(&mesh->vertexBuffer_NOR), + static_cast(&mesh->vertexBuffer_BON), + }; + const GPUUnorderedResource* sos[] = { + static_cast(&mesh->streamoutBuffer_POS), + static_cast(&mesh->streamoutBuffer_NOR), + static_cast(&mesh->streamoutBuffer_PRE), + }; + + GetDevice()->BindResourcesCS(vbs, SKINNINGSLOT_IN_VERTEX_POS, ARRAYSIZE(vbs), threadID); + GetDevice()->BindUnorderedAccessResourcesCS(sos, 0, ARRAYSIZE(sos), threadID); + + GetDevice()->Dispatch((UINT)ceilf((float)mesh->vertices_POS.size() / SKINNING_COMPUTE_THREADCOUNT), 1, 1, threadID); + + } + else if (mesh->hasDynamicVB()) + { + // Upload CPU skinned vertex buffer (Soft body VB) + mesh->bufferOffset_POS = GetDevice()->AppendRingBuffer(dynamicVertexBufferPool, mesh->vertices_Transformed_POS.data(), sizeof(Mesh::Vertex_POS)*mesh->vertices_Transformed_POS.size(), threadID); + mesh->bufferOffset_NOR = GetDevice()->AppendRingBuffer(dynamicVertexBufferPool, mesh->vertices_Transformed_NOR.data(), sizeof(Mesh::Vertex_NOR)*mesh->vertices_Transformed_NOR.size(), threadID); + mesh->bufferOffset_PRE = GetDevice()->AppendRingBuffer(dynamicVertexBufferPool, mesh->vertices_Transformed_PRE.data(), sizeof(Mesh::Vertex_POS)*mesh->vertices_Transformed_PRE.size(), threadID); + } + } + } + + if (streamOutSetUp) + { + // Unload skinning shader + GetDevice()->BindCS(nullptr, threadID); + GetDevice()->UnBindUnorderedAccessResources(0, 3, threadID); + GetDevice()->UnBindResources(SKINNINGSLOT_IN_VERTEX_POS, 4, threadID); + } + + } + GetDevice()->EventEnd(threadID); + wiProfiler::GetInstance().EndRange(threadID); // skinning + + // Render out of date environment probes: + RefreshEnvProbes(threadID); + + // Bind environment probes: + { + const GPUResource* envMaps[] = { + globalEnvProbes[0] == nullptr ? enviroMap : globalEnvProbes[0]->cubeMap.GetTexture(), + globalEnvProbes[1] == nullptr ? enviroMap : globalEnvProbes[1]->cubeMap.GetTexture(), + }; + GetDevice()->BindResourcesPS(envMaps, TEXSLOT_ENV0, ARRAYSIZE(envMaps), threadID); + GetDevice()->BindResourcesCS(envMaps, TEXSLOT_ENV0, ARRAYSIZE(envMaps), threadID); + } } void wiRenderer::OcclusionCulling_Render(GRAPHICSTHREAD threadID) { @@ -2765,7 +2764,7 @@ void wiRenderer::DrawDebugEmitters(Camera* camera, GRAPHICSTHREAD threadID) GetDevice()->BindRasterizerState(rasterizers[RSTYPE_WIRE_DOUBLESIDED_SMOOTH], threadID); GetDevice()->BindDepthStencilState(depthStencils[DSSTYPE_DEPTHREAD], STENCILREF_EMPTY, threadID); - GetDevice()->BindBlendState(blendStates[BSTYPE_INVERSE], threadID); + GetDevice()->BindBlendState(blendStates[BSTYPE_OPAQUE], threadID); GetDevice()->BindPS(pixelShaders[PSTYPE_OBJECT_DEBUG], threadID); @@ -2777,7 +2776,7 @@ void wiRenderer::DrawDebugEmitters(Camera* camera, GRAPHICSTHREAD threadID) if (x->object != nullptr && x->object->mesh != nullptr) { sb.mTransform = XMMatrixTranspose(XMLoadFloat4x4(&x->object->world)*camera->GetViewProjection()); - sb.mColor = XMFLOAT4(1, 1, 1, 1); + sb.mColor = XMFLOAT4(0, 1, 0, 1); GetDevice()->UpdateBuffer(constantBuffers[CBTYPE_MISC], &sb, threadID); const GPUBuffer* vbs[] = { @@ -2789,10 +2788,7 @@ void wiRenderer::DrawDebugEmitters(Camera* camera, GRAPHICSTHREAD threadID) GetDevice()->BindVertexBuffers(vbs, 0, ARRAYSIZE(vbs), strides, nullptr, threadID); GetDevice()->BindIndexBuffer(&x->object->mesh->indexBuffer, x->object->mesh->GetIndexFormat(), 0, threadID); - for (auto& y : x->object->mesh->subsets) - { - GetDevice()->DrawIndexed((int)y.subsetIndices.size(), y.indexBufferOffset, 0, threadID); - } + GetDevice()->DrawIndexed((int)x->object->mesh->indices.size(), 0, 0, threadID); } } diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index 862b75995..810de693f 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wiVersion // minor features, major updates const int minor = 13; // minor bug fixes, alterations, refactors, updates - const int revision = 70; + const int revision = 71; long GetVersion()